From a2566b3ebd93e0bbc55a920a7be08054a9377f11 Mon Sep 17 00:00:00 2001 From: Magnus Auvinen Date: Sat, 15 Dec 2007 10:24:49 +0000 Subject: cleaned up code structure a bit --- src/engine/client/client.c | 1146 -------------------------------------- src/engine/client/ec_client.c | 1146 ++++++++++++++++++++++++++++++++++++++ src/engine/client/ec_gfx.c | 1018 +++++++++++++++++++++++++++++++++ src/engine/client/ec_inp.c | 210 +++++++ src/engine/client/ec_snd.c | 441 +++++++++++++++ src/engine/client/ec_srvbrowse.c | 404 ++++++++++++++ src/engine/client/ec_ui.c | 277 +++++++++ src/engine/client/ec_ui.h | 36 ++ src/engine/client/gfx.c | 1018 --------------------------------- src/engine/client/inp.c | 210 ------- src/engine/client/snd.c | 441 --------------- src/engine/client/srvbrowse.c | 404 -------------- src/engine/client/ui.c | 277 --------- src/engine/client/ui.h | 36 -- src/engine/compression.c | 147 ----- src/engine/compression.h | 11 - src/engine/config.c | 196 ------- src/engine/config.h | 41 -- src/engine/config_variables.h | 56 -- src/engine/datafile.c | 630 --------------------- src/engine/datafile.h | 27 - src/engine/detect.h | 115 ---- src/engine/e_compression.c | 147 +++++ src/engine/e_compression.h | 11 + src/engine/e_config.c | 196 +++++++ src/engine/e_config.h | 41 ++ src/engine/e_config_variables.h | 56 ++ src/engine/e_datafile.c | 630 +++++++++++++++++++++ src/engine/e_datafile.h | 27 + src/engine/e_detect.h | 115 ++++ src/engine/e_engine.c | 66 +++ src/engine/e_engine.h | 5 + src/engine/e_interface.h | 858 ++++++++++++++++++++++++++++ src/engine/e_keynames.c | 524 +++++++++++++++++ src/engine/e_keys.h | 88 +++ src/engine/e_map.c | 61 ++ src/engine/e_memheap.c | 102 ++++ src/engine/e_memheap.h | 6 + src/engine/e_msg.c | 55 ++ src/engine/e_network.c | 937 +++++++++++++++++++++++++++++++ src/engine/e_network.h | 113 ++++ src/engine/e_packer.c | 91 +++ src/engine/e_packer.h | 35 ++ src/engine/e_protocol.h | 60 ++ src/engine/e_snapshot.c | 448 +++++++++++++++ src/engine/e_snapshot.h | 91 +++ src/engine/e_system.c | 723 ++++++++++++++++++++++++ src/engine/e_system.h | 531 ++++++++++++++++++ src/engine/engine.c | 66 --- src/engine/engine.h | 5 - src/engine/external/pa.c | 2 +- src/engine/interface.h | 858 ---------------------------- src/engine/keynames.c | 524 ----------------- src/engine/keys.h | 88 --- src/engine/map.c | 61 -- src/engine/memheap.c | 102 ---- src/engine/memheap.h | 6 - src/engine/msg.c | 55 -- src/engine/network.c | 937 ------------------------------- src/engine/network.h | 113 ---- src/engine/packer.c | 91 --- src/engine/packer.h | 35 -- src/engine/protocol.h | 60 -- src/engine/server/es_server.c | 901 ++++++++++++++++++++++++++++++ src/engine/server/server.c | 901 ------------------------------ src/engine/snapshot.c | 448 --------------- src/engine/snapshot.h | 91 --- src/engine/system.c | 723 ------------------------ src/engine/system.h | 531 ------------------ 69 files changed, 10451 insertions(+), 10451 deletions(-) delete mode 100644 src/engine/client/client.c create mode 100644 src/engine/client/ec_client.c create mode 100644 src/engine/client/ec_gfx.c create mode 100644 src/engine/client/ec_inp.c create mode 100644 src/engine/client/ec_snd.c create mode 100644 src/engine/client/ec_srvbrowse.c create mode 100644 src/engine/client/ec_ui.c create mode 100644 src/engine/client/ec_ui.h delete mode 100644 src/engine/client/gfx.c delete mode 100644 src/engine/client/inp.c delete mode 100644 src/engine/client/snd.c delete mode 100644 src/engine/client/srvbrowse.c delete mode 100644 src/engine/client/ui.c delete mode 100644 src/engine/client/ui.h delete mode 100644 src/engine/compression.c delete mode 100644 src/engine/compression.h delete mode 100644 src/engine/config.c delete mode 100644 src/engine/config.h delete mode 100644 src/engine/config_variables.h delete mode 100644 src/engine/datafile.c delete mode 100644 src/engine/datafile.h delete mode 100644 src/engine/detect.h create mode 100644 src/engine/e_compression.c create mode 100644 src/engine/e_compression.h create mode 100644 src/engine/e_config.c create mode 100644 src/engine/e_config.h create mode 100644 src/engine/e_config_variables.h create mode 100644 src/engine/e_datafile.c create mode 100644 src/engine/e_datafile.h create mode 100644 src/engine/e_detect.h create mode 100644 src/engine/e_engine.c create mode 100644 src/engine/e_engine.h create mode 100644 src/engine/e_interface.h create mode 100644 src/engine/e_keynames.c create mode 100644 src/engine/e_keys.h create mode 100644 src/engine/e_map.c create mode 100644 src/engine/e_memheap.c create mode 100644 src/engine/e_memheap.h create mode 100644 src/engine/e_msg.c create mode 100644 src/engine/e_network.c create mode 100644 src/engine/e_network.h create mode 100644 src/engine/e_packer.c create mode 100644 src/engine/e_packer.h create mode 100644 src/engine/e_protocol.h create mode 100644 src/engine/e_snapshot.c create mode 100644 src/engine/e_snapshot.h create mode 100644 src/engine/e_system.c create mode 100644 src/engine/e_system.h delete mode 100644 src/engine/engine.c delete mode 100644 src/engine/engine.h delete mode 100644 src/engine/interface.h delete mode 100644 src/engine/keynames.c delete mode 100644 src/engine/keys.h delete mode 100644 src/engine/map.c delete mode 100644 src/engine/memheap.c delete mode 100644 src/engine/memheap.h delete mode 100644 src/engine/msg.c delete mode 100644 src/engine/network.c delete mode 100644 src/engine/network.h delete mode 100644 src/engine/packer.c delete mode 100644 src/engine/packer.h delete mode 100644 src/engine/protocol.h create mode 100644 src/engine/server/es_server.c delete mode 100644 src/engine/server/server.c delete mode 100644 src/engine/snapshot.c delete mode 100644 src/engine/snapshot.h delete mode 100644 src/engine/system.c delete mode 100644 src/engine/system.h (limited to 'src/engine') diff --git a/src/engine/client/client.c b/src/engine/client/client.c deleted file mode 100644 index 646ca4b4..00000000 --- a/src/engine/client/client.c +++ /dev/null @@ -1,1146 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include "ui.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -const int prediction_margin = 10; /* magic network prediction value */ - -/* - Server Time - Client Mirror Time - Client Predicted Time - - Snapshot Latency - Downstream latency - - Prediction Latency - Upstream latency -*/ - -/* network client, must be accessible from other parts like the server browser */ -NETCLIENT *net; - -/* TODO: ugly, fix me */ -extern void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info); - - -static int snapshot_part; -static int64 local_start_time; - -static int debug_font; -static float frametime = 0.0001f; -static float frametime_low = 1.0f; -static float frametime_high = 0.0f; -static int frames = 0; -static NETADDR4 server_address; -static int window_must_refocus = 0; -static int snaploss = 0; -static int snapcrcerrors = 0; - -static int ack_game_tick = -1; -static int current_recv_tick = 0; - -/* current time */ -static int current_tick = 0; -static float intratick = 0; - -/* predicted time */ -static int current_predtick = 0; -static float intrapredtick = 0; - -static struct /* TODO: handle input better */ -{ - int data[MAX_INPUT_SIZE]; /* the input data */ - int tick; /* the tick that the input is for */ - int64 game_time; /* prediction latency when we sent this input */ - int64 time; -} inputs[200]; -static int current_input = 0; - -enum -{ - GRAPH_MAX=128 -}; - -typedef struct -{ - float min, max; - float values[GRAPH_MAX]; - int index; -} GRAPH; - -static void graph_init(GRAPH *g, float min, float max) -{ - g->min = min; - g->max = max; - g->index = 0; -} - -static void graph_add(GRAPH *g, float v) -{ - g->index = (g->index+1)&(GRAPH_MAX-1); - g->values[g->index] = v; -} - -static void graph_render(GRAPH *g, float x, float y, float w, float h) -{ - int i; - gfx_texture_set(-1); - - gfx_quads_begin(); - gfx_setcolor(0, 0, 0, 1); - gfx_quads_drawTL(x, y, w, h); - gfx_quads_end(); - - gfx_lines_begin(); - gfx_setcolor(0.95f, 0.95f, 0.95f, 1); - gfx_lines_draw(x, y+h/2, x+w, y+h/2); - gfx_setcolor(0.5f, 0.5f, 0.5f, 1); - gfx_lines_draw(x, y+(h*3)/4, x+w, y+(h*3)/4); - gfx_lines_draw(x, y+h/4, x+w, y+h/4); - for(i = 1; i < GRAPH_MAX; i++) - { - float a0 = (i-1)/(float)GRAPH_MAX; - float a1 = i/(float)GRAPH_MAX; - int i0 = (g->index+i-1)&(GRAPH_MAX-1); - int i1 = (g->index+i)&(GRAPH_MAX-1); - - float v0 = g->values[i0]; - float v1 = g->values[i1]; - - gfx_setcolor(0, 1, 0, 1); - gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); - } - gfx_lines_end(); -} - -typedef struct -{ - int64 snap; - int64 current; - int64 target; - - int64 rlast; - int64 tlast; - GRAPH graph; -} SMOOTHTIME; - -static void st_init(SMOOTHTIME *st, int64 target) -{ - st->snap = time_get(); - st->current = target; - st->target = target; - graph_init(&st->graph, 0.0f, 1.0f); -} - -static int64 st_get(SMOOTHTIME *st, int64 now) -{ - float adjust_speed, a; - int64 c = st->current + (now - st->snap); - int64 t = st->target + (now - st->snap); - int64 r; - - /* it's faster to adjust upward instead of downward */ - /* we might need to adjust these abit */ - adjust_speed = 0.25f; /*0.99f;*/ - if(t > c) - adjust_speed = 500.0f; - - a = ((now-st->snap)/(float)time_freq()) * adjust_speed; - if(a > 1.0f) - a = 1.0f; - - r = c + (int64)((t-c)*a); - - graph_add(&st->graph, a+0.5f); - - return r; -} - -static void st_update(SMOOTHTIME *st, int64 target) -{ - int64 now = time_get(); - st->current = st_get(st, now); - st->snap = now; - st->target = target; -} - -SMOOTHTIME game_time; -SMOOTHTIME predicted_time; - -GRAPH intra_graph; -GRAPH predict_graph; - -/* --- input snapping --- */ -static int input_data[MAX_INPUT_SIZE] = {0}; -static int input_data_size; -static int input_is_changed = 1; -static GRAPH input_late_graph; -void snap_input(void *data, int size) -{ - if(input_data_size != size || memcmp(input_data, data, size)) - input_is_changed = 1; - mem_copy(input_data, data, size); - input_data_size = size; -} - -/* -- snapshot handling --- */ -enum -{ - NUM_SNAPSHOT_TYPES=2 -}; - -SNAPSTORAGE snapshot_storage; -static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES]; -static int recived_snapshots; -static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; - -/* --- */ - -const void *snap_get_item(int snapid, int index, SNAP_ITEM *item) -{ - SNAPSHOT_ITEM *i; - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshot_get_item(snapshots[snapid]->snap, index); - item->type = snapitem_type(i); - item->id = snapitem_id(i); - return (void *)snapitem_data(i); -} - -const void *snap_find_item(int snapid, int type, int id) -{ - /* TODO: linear search. should be fixed. */ - int i; - for(i = 0; i < snapshots[snapid]->snap->num_items; i++) - { - SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->snap, i); - if(snapitem_type(itm) == type && snapitem_id(itm) == id) - return (void *)snapitem_data(itm); - } - return 0x0; -} - -int snap_num_items(int snapid) -{ - dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - return snapshots[snapid]->snap->num_items; -} - -/* ------ time functions ------ */ -float client_intratick() { return intratick; } -float client_intrapredtick() { return intrapredtick; } -int client_tick() { return current_tick; } -int client_predtick() { return current_predtick; } -int client_tickspeed() { return SERVER_TICK_SPEED; } -float client_frametime() { return frametime; } -float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); } - -/* ----- send functions ----- */ -int client_send_msg() -{ - const MSG_INFO *info = msg_get_info(); - NETPACKET packet; - mem_zero(&packet, sizeof(NETPACKET)); - - packet.client_id = 0; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags = PACKETFLAG_VITAL; - - netclient_send(net, &packet); - return 0; -} - -static void client_send_info() -{ - msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL); - msg_pack_string(modc_net_version(), 128); - msg_pack_string(config.player_name, 128); - msg_pack_string(config.clan_name, 128); - msg_pack_string(config.password, 128); - msg_pack_end(); - client_send_msg(); -} - - -static void client_send_entergame() -{ - msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL); - msg_pack_end(); - client_send_msg(); -} - -static void client_send_ready() -{ - msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL); - msg_pack_end(); - client_send_msg(); -} - -void client_rcon(const char *cmd) -{ - msg_pack_start_system(NETMSG_CMD, MSGFLAG_VITAL); - msg_pack_string(config.rcon_password, 32); - msg_pack_string(cmd, 256); - msg_pack_end(); - client_send_msg(); -} - -int client_connection_problems() -{ - return netclient_gotproblems(net); -} - -static void client_send_input() -{ - int64 now = time_get(); - int i; - - if(current_predtick <= 0) - return; - - msg_pack_start_system(NETMSG_INPUT, 0); - msg_pack_int(ack_game_tick); - msg_pack_int(current_predtick); - msg_pack_int(input_data_size); - - inputs[current_input].tick = current_predtick; - inputs[current_input].game_time = st_get(&predicted_time, now); - inputs[current_input].time = now; - - for(i = 0; i < input_data_size/4; i++) - { - inputs[current_input].data[i] = input_data[i]; - msg_pack_int(input_data[i]); - } - - current_input++; - current_input%=200; - - msg_pack_end(); - client_send_msg(); -} - -/* TODO: OPT: do this alot smarter! */ -int *client_get_input(int tick) -{ - int i; - int best = -1; - for(i = 0; i < 200; i++) - { - if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick)) - best = i; - } - - if(best != -1) - return (int *)inputs[best].data; - return 0; -} - -/* ------ state handling ----- */ -static int state = CLIENTSTATE_OFFLINE; -int client_state() { return state; } -static void client_set_state(int s) -{ - int old = state; - if(config.debug) - dbg_msg("client", "state change. last=%d current=%d", state, s); - state = s; - if(old != s) - modc_statechange(state, old); -} - -/* called when the map is loaded and we should init for a new round */ -static void client_on_enter_game() -{ - /* reset input */ - int i; - for(i = 0; i < 200; i++) - inputs[i].tick = -1; - current_input = 0; - - /* reset snapshots */ - snapshots[SNAP_CURRENT] = 0; - snapshots[SNAP_PREV] = 0; - snapstorage_purge_all(&snapshot_storage); - recived_snapshots = 0; - current_predtick = 0; - current_recv_tick = 0; -} - -void client_entergame() -{ - /* now we will wait for two snapshots */ - /* to finish the connection */ - client_send_entergame(); - client_on_enter_game(); -} - -void client_connect(const char *server_address_str) -{ - char buf[512]; - const char *port_str = 0; - int k; - int port = 8303; - - dbg_msg("client", "connecting to '%s'", server_address_str); - - strncpy(buf, server_address_str, 512); - - for(k = 0; buf[k]; k++) - { - if(buf[k] == ':') - { - port_str = &(buf[k+1]); - buf[k] = 0; - break; - } - } - - if(port_str) - port = atoi(port_str); - - if(net_host_lookup(buf, port, &server_address) != 0) - dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); - - netclient_connect(net, &server_address); - client_set_state(CLIENTSTATE_CONNECTING); - - graph_init(&intra_graph, 0.0f, 1.0f); - graph_init(&input_late_graph, 0.0f, 1.0f); - graph_init(&predict_graph, 0.0f, 200.0f); - -} - -void client_disconnect_with_reason(const char *reason) -{ - netclient_disconnect(net, reason); - client_set_state(CLIENTSTATE_OFFLINE); - map_unload(); -} - -void client_disconnect() -{ - netclient_disconnect(net, 0); - client_set_state(CLIENTSTATE_OFFLINE); - map_unload(); -} - -static int client_load_data() -{ - debug_font = gfx_load_texture("data/debug_font.png"); - return 1; -} - -extern int snapshot_data_rate[0xffff]; -extern int snapshot_data_updates[0xffff]; - -static void client_debug_render() -{ - static NETSTATS prev, current; - static int64 last_snap = 0; - static float frametime_avg = 0; - char buffer[512]; - - if(!config.debug) - return; - - gfx_blend_normal(); - gfx_texture_set(debug_font); - gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); - - if(time_get()-last_snap > time_freq()/10) - { - last_snap = time_get(); - prev = current; - netclient_stats(net, ¤t); - } - - frametime_avg = frametime_avg*0.9f + frametime*0.1f; - sprintf(buffer, "ticks: %8d %8d send: %6d recv: %6d snaploss: %d mem %dk gfxmem: %dk fps: %3d", - current_tick, current_predtick, - (current.send_bytes-prev.send_bytes)*10, - (current.recv_bytes-prev.recv_bytes)*10, - snaploss, - mem_allocated()/1024, - gfx_memory_usage()/1024, - (int)(1.0f/frametime_avg)); - gfx_quads_text(2, 2, 16, buffer); - - sprintf(buffer, "ui: %p %p", ui_hot_item(), ui_active_item()); - gfx_quads_text(2, 16, 16, buffer); - - - /* render rates */ - { - int i; - for(i = 0; i < 256; i++) - { - if(snapshot_data_rate[i]) - { - sprintf(buffer, "%4d : %8d %8d %8d", i, snapshot_data_rate[i]/8, snapshot_data_updates[i], - (snapshot_data_rate[i]/snapshot_data_updates[i])/8); - gfx_quads_text(2, 100+i*8, 16, buffer); - } - } - } - - /* render graphs */ - gfx_mapscreen(0,0,400.0f,300.0f); - graph_render(&predict_graph, 300, 10, 90, 50); - graph_render(&predicted_time.graph, 300, 10+50+10, 90, 50); - - graph_render(&intra_graph, 300, 10+50+10+50+10, 90, 50); - graph_render(&input_late_graph, 300, 10+50+10+50+10+50+10, 90, 50); - -} - -void client_quit() -{ - client_set_state(CLIENTSTATE_QUITING); -} - -const char *client_error_string() -{ - return netclient_error_string(net); -} - -static void client_render() -{ - gfx_clear(0.0f,0.0f,0.0f); - modc_render(); - client_debug_render(); -} - -static const char *client_load_map(const char *mapname, int wanted_crc) -{ - static char errormsg[128]; - DATAFILE *df; - char buf[512]; - int crc; - - dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc); - client_set_state(CLIENTSTATE_LOADING); - - sprintf(buf, "data/maps/%s.map", mapname); - df = datafile_load(buf); - if(!df) - { - sprintf(errormsg, "map '%s' not found", mapname); - return errormsg; - } - - /* get the crc of the map */ - crc = datafile_crc(buf); - if(crc != wanted_crc) - { - datafile_unload(df); - sprintf(errormsg, "map differs from the server. %08x != %08x", crc, wanted_crc); - return errormsg; - } - - map_set(df); - return NULL; -} - -static void client_process_packet(NETPACKET *packet) -{ - if(packet->client_id == -1) - { - /* connectionlesss */ - if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && - memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) - { - int size = packet->data_size-sizeof(SERVERBROWSE_LIST); - int num = size/sizeof(NETADDR4); - NETADDR4 *addrs = (NETADDR4 *)((char*)packet->data+sizeof(SERVERBROWSE_LIST)); - int i; - - for(i = 0; i < num; i++) - { - NETADDR4 addr = addrs[i]; - SERVER_INFO info = {0}; - -#if defined(CONF_ARCH_ENDIAN_BIG) - const char *tmp = (const char *)&addr.port; - addr.port = (tmp[1]<<8) | tmp[0]; -#endif - - info.latency = 999; - sprintf(info.address, "%d.%d.%d.%d:%d", - addr.ip[0], addr.ip[1], addr.ip[2], - addr.ip[3], addr.port); - sprintf(info.name, "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ - addr.ip[0], addr.ip[1], addr.ip[2], - addr.ip[3], addr.port); - - client_serverbrowse_set(addrs+i, 1, &info); - } - } - - if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && - memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) - { - /* we got ze info */ - UNPACKER up; - SERVER_INFO info = {0}; - int i; - - unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); - - strncpy(info.version, unpacker_get_string(&up), 32); - strncpy(info.name, unpacker_get_string(&up), 64); - strncpy(info.map, unpacker_get_string(&up), 32); - info.game_type = atol(unpacker_get_string(&up)); - info.flags = atol(unpacker_get_string(&up)); - info.progression = atol(unpacker_get_string(&up)); - info.num_players = atol(unpacker_get_string(&up)); - info.max_players = atol(unpacker_get_string(&up)); - sprintf(info.address, "%d.%d.%d.%d:%d", - packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], - packet->address.ip[3], packet->address.port); - - for(i = 0; i < info.num_players; i++) - { - strncpy(info.player_names[i], unpacker_get_string(&up), 48); - info.player_scores[i] = atol(unpacker_get_string(&up)); - } - - /* TODO: unpack players aswell */ - client_serverbrowse_set(&packet->address, 0, &info); - } - } - else - { - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - - if(sys) - { - /* system message */ - if(msg == NETMSG_MAP) - { - const char *map = msg_unpack_string(); - int map_crc = msg_unpack_int(); - const char *error = client_load_map(map, map_crc); - - if(!error) - { - dbg_msg("client/network", "loading done"); - client_send_ready(); - modc_connected(); - } - else - { - client_disconnect_with_reason(error); - } - } - else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY) - { - /*dbg_msg("client/network", "got snapshot"); */ - int num_parts = 1; - int part = 0; - int game_tick = msg_unpack_int(); - int delta_tick = game_tick-msg_unpack_int(); - int input_predtick = msg_unpack_int(); - int time_left = msg_unpack_int(); - int part_size = 0; - int crc = 0; - int complete_size = 0; - - if(msg == NETMSG_SNAP) - { - num_parts = msg_unpack_int(); - part = msg_unpack_int(); - } - - if(msg != NETMSG_SNAPEMPTY) - { - crc = msg_unpack_int(); - part_size = msg_unpack_int(); - } - - /* TODO: adjust our prediction time */ - if(time_left) - { - int k; - - graph_add(&input_late_graph, time_left/100.0f+0.5f); - - if(time_left < 0) - dbg_msg("client", "input was late with %d ms", time_left); - - for(k = 0; k < 200; k++) /* TODO: do this better */ - { - if(inputs[k].tick == input_predtick) - { - /*-1000/50 prediction_margin */ - int64 target = inputs[k].game_time + (time_get() - inputs[k].time); - st_update(&predicted_time, target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq())); - break; - } - } - } - - if(snapshot_part == part && game_tick > current_recv_tick) - { - /* TODO: clean this up abit */ - const char *d = (const char *)msg_unpack_raw(part_size); - mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); - snapshot_part++; - - if(snapshot_part == num_parts) - { - static SNAPSHOT emptysnap; - SNAPSHOT *deltashot = &emptysnap; - int purgetick; - void *deltadata; - int deltasize; - unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; - int snapsize; - - complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size; - - snapshot_part = 0; - - /* find snapshot that we should use as delta */ - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - /* find delta */ - if(delta_tick >= 0) - { - int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot); - - if(deltashot_size < 0) - { - /* 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 */ - /* TODO: combine this with the input message */ - ack_game_tick = -1; - return; - } - } - - /* decompress snapshot */ - deltadata = snapshot_empty_delta(); - deltasize = sizeof(int)*3; - - if(complete_size) - { - int compsize = zerobit_decompress(snapshot_incomming_data, complete_size, tmpbuffer); - int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); - deltadata = tmpbuffer2; - deltasize = intsize; - } - - /*dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); */ - - purgetick = delta_tick; - snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize); - if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc) - { - if(config.debug) - dbg_msg("client", "snapshot crc error %d", snapcrcerrors); - snapcrcerrors++; - if(snapcrcerrors > 10) - { - /* to many errors, send reset */ - ack_game_tick = -1; - client_send_input(); - snapcrcerrors = 0; - } - return; - } - else - { - if(snapcrcerrors) - snapcrcerrors--; - } - - /* purge old snapshots */ - purgetick = delta_tick; - if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick) - purgetick = snapshots[SNAP_PREV]->tick; - snapstorage_purge_until(&snapshot_storage, purgetick); - /*client_snapshot_purge_until(game_tick-50); */ - - /* add new */ - snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3); - - /* apply snapshot, cycle pointers */ - recived_snapshots++; - - - if(current_recv_tick > 0) - snaploss += game_tick-current_recv_tick-1; - current_recv_tick = game_tick; - - /* we got two snapshots until we see us self as connected */ - if(recived_snapshots == 2) - { - /* start at 200ms and work from there */ - st_init(&predicted_time, game_tick*time_freq()/50); - st_init(&game_time, (game_tick-1)*time_freq()/50); - snapshots[SNAP_PREV] = snapshot_storage.first; - snapshots[SNAP_CURRENT] = snapshot_storage.last; - local_start_time = time_get(); - client_set_state(CLIENTSTATE_ONLINE); - } - - { - int64 now = time_get(); - graph_add(&predict_graph, (st_get(&predicted_time, now)-st_get(&game_time, now))/(float)time_freq()); - } - - st_update(&game_time, (game_tick-1)*time_freq()/50); - - /* ack snapshot */ - ack_game_tick = game_tick; - } - } - else - { - dbg_msg("client", "snapsht reset!"); - snapshot_part = 0; - } - } - } - else - { - /* game message */ - modc_message(msg); - } - } -} - -static void client_pump_network() -{ - NETPACKET packet; - - netclient_update(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(); - } - - /* process packets */ - while(netclient_recv(net, &packet)) - client_process_packet(&packet); -} - -static void client_update() -{ - /* switch snapshot */ - if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) - { - int repredict = 0; - int64 now = st_get(&game_time, time_get()); - int64 pred_now = st_get(&predicted_time, time_get()); - - while(1) - { - SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT]; - int64 tickstart = (cur->tick)*time_freq()/50; - - if(tickstart < now) - { - SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next; - if(next) - { - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = next; - - /* set tick */ - current_tick = snapshots[SNAP_CURRENT]->tick; - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - modc_newsnapshot(); - repredict = 1; - } - } - else - break; - } - else - break; - } - - if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) - { - int64 curtick_start = (snapshots[SNAP_CURRENT]->tick)*time_freq()/50; - int64 prevtick_start = (snapshots[SNAP_PREV]->tick)*time_freq()/50; - /*tg_add(&predicted_time_graph, pred_now, 0); */ - int prev_pred_tick = (int)(pred_now*50/time_freq()); - int new_pred_tick = prev_pred_tick+1; - static float last_intrapred = 0; - - intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); - - graph_add(&intra_graph, intratick*0.25f); - - curtick_start = new_pred_tick*time_freq()/50; - prevtick_start = prev_pred_tick*time_freq()/50; - intrapredtick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); - - - if(new_pred_tick > current_predtick) - { - last_intrapred = intrapredtick; - current_predtick = new_pred_tick; - repredict = 1; - - /* send input */ - client_send_input(); - } - - if(intrapredtick < last_intrapred) - dbg_msg("client", "prediction time goes backwards, that can't be good"); - last_intrapred = intrapredtick; - } - - /* only do sane predictions */ - if(repredict) - { - if(current_predtick > current_tick && current_predtick < current_tick+50) - modc_predict(); - } - } - - /* STRESS TEST: join the server again */ - if(client_state() == CLIENTSTATE_OFFLINE && config.dbg_stress && (frames%100) == 0) - client_connect(config.dbg_stress_server); - - /* pump the network */ - client_pump_network(); - - /* update the server browser */ - client_serverbrowse_update(); -} - -extern int editor_update_and_render(); -extern void editor_init(); - -static void client_run() -{ - NETADDR4 bindaddr; - int64 reporttime = time_get(); - int64 reportinterval = time_freq()*1; - int editor_active = 0; - - local_start_time = time_get(); - snapshot_part = 0; - - /* init graphics and sound */ - if(!gfx_init()) - return; - - /* init the editor */ - editor_init(); - - /* sound is allowed to fail */ - snd_init(); - - /* load data */ - if(!client_load_data()) - return; - - /* init the mod */ - modc_init(); - dbg_msg("client", "version %s", modc_net_version()); - - /* open socket */ - mem_zero(&bindaddr, sizeof(bindaddr)); - net = netclient_open(bindaddr, 0); - - /* connect to the server if wanted */ - /* - if(config.cl_connect[0] != 0) - client_connect(config.cl_connect); - config.cl_connect[0] = 0; - */ - - inp_mouse_mode_relative(); - - while (1) - { - int64 frame_start_time = time_get(); - frames++; - - /* update input */ - inp_update(); - - /* refocus */ - if(!gfx_window_active()) - { - if(window_must_refocus == 0) - inp_mouse_mode_absolute(); - window_must_refocus = 1; - } - - if(window_must_refocus && gfx_window_active()) - { - if(window_must_refocus < 3) - { - inp_mouse_mode_absolute(); - window_must_refocus++; - } - - if(inp_key_pressed(KEY_MOUSE_1)) - { - inp_mouse_mode_relative(); - window_must_refocus = 0; - } - } - - /* screenshot button */ - if(inp_key_down(config.key_screenshot)) - gfx_screenshot(); - - /* some debug keys */ - /* - if(config.debug) - { - if(inp_key_pressed(KEY_F1)) - inp_mouse_mode_absolute(); - if(inp_key_pressed(KEY_F2)) - inp_mouse_mode_relative(); - - if(inp_key_pressed(KEY_F5)) - { - ack_game_tick = -1; - client_send_input(); - } - }*/ - - /* panic quit button */ - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('Q')) - break; - - if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('E')) - editor_active = editor_active^1; - - if(!gfx_window_open()) - break; - - /* render */ - if(editor_active) - { - client_update(); - editor_update_and_render(); - gfx_swap(); - } - else - { - client_update(); - - if(config.dbg_stress) - { - if((frames%10) == 0) - { - client_render(); - gfx_swap(); - } - } - else - { - client_render(); - gfx_swap(); - } - } - - /* check conditions */ - if(client_state() == CLIENTSTATE_QUITING) - break; - - /* be nice */ - if(config.cl_cpu_throttle || !gfx_window_active()) - thread_sleep(1); - - if(reporttime < time_get()) - { - if(config.debug) - { - dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", - frames/(float)(reportinterval/time_freq()), - 1.0f/frametime_high, - 1.0f/frametime_low, - netclient_state(net)); - } - frametime_low = 1; - frametime_high = 0; - frames = 0; - reporttime += reportinterval; - } - - /* update frametime */ - frametime = (time_get()-frame_start_time)/(float)time_freq(); - if(frametime < frametime_low) - frametime_low = frametime; - if(frametime > frametime_high) - frametime_high = frametime; - } - - modc_shutdown(); - client_disconnect(); - - gfx_shutdown(); - snd_shutdown(); -} - - -int editor_main(int argc, char **argv); - -int main(int argc, char **argv) -{ - /* init the engine */ - dbg_msg("client", "starting..."); - engine_init("Teewars", argc, argv); - - client_run(); - - engine_writeconfig(); - return 0; -} diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c new file mode 100644 index 00000000..78e7877c --- /dev/null +++ b/src/engine/client/ec_client.c @@ -0,0 +1,1146 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include "ec_ui.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +const int prediction_margin = 10; /* magic network prediction value */ + +/* + Server Time + Client Mirror Time + Client Predicted Time + + Snapshot Latency + Downstream latency + + Prediction Latency + Upstream latency +*/ + +/* network client, must be accessible from other parts like the server browser */ +NETCLIENT *net; + +/* TODO: ugly, fix me */ +extern void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info); + + +static int snapshot_part; +static int64 local_start_time; + +static int debug_font; +static float frametime = 0.0001f; +static float frametime_low = 1.0f; +static float frametime_high = 0.0f; +static int frames = 0; +static NETADDR4 server_address; +static int window_must_refocus = 0; +static int snaploss = 0; +static int snapcrcerrors = 0; + +static int ack_game_tick = -1; +static int current_recv_tick = 0; + +/* current time */ +static int current_tick = 0; +static float intratick = 0; + +/* predicted time */ +static int current_predtick = 0; +static float intrapredtick = 0; + +static struct /* TODO: handle input better */ +{ + int data[MAX_INPUT_SIZE]; /* the input data */ + int tick; /* the tick that the input is for */ + int64 game_time; /* prediction latency when we sent this input */ + int64 time; +} inputs[200]; +static int current_input = 0; + +enum +{ + GRAPH_MAX=128 +}; + +typedef struct +{ + float min, max; + float values[GRAPH_MAX]; + int index; +} GRAPH; + +static void graph_init(GRAPH *g, float min, float max) +{ + g->min = min; + g->max = max; + g->index = 0; +} + +static void graph_add(GRAPH *g, float v) +{ + g->index = (g->index+1)&(GRAPH_MAX-1); + g->values[g->index] = v; +} + +static void graph_render(GRAPH *g, float x, float y, float w, float h) +{ + int i; + gfx_texture_set(-1); + + gfx_quads_begin(); + gfx_setcolor(0, 0, 0, 1); + gfx_quads_drawTL(x, y, w, h); + gfx_quads_end(); + + gfx_lines_begin(); + gfx_setcolor(0.95f, 0.95f, 0.95f, 1); + gfx_lines_draw(x, y+h/2, x+w, y+h/2); + gfx_setcolor(0.5f, 0.5f, 0.5f, 1); + gfx_lines_draw(x, y+(h*3)/4, x+w, y+(h*3)/4); + gfx_lines_draw(x, y+h/4, x+w, y+h/4); + for(i = 1; i < GRAPH_MAX; i++) + { + float a0 = (i-1)/(float)GRAPH_MAX; + float a1 = i/(float)GRAPH_MAX; + int i0 = (g->index+i-1)&(GRAPH_MAX-1); + int i1 = (g->index+i)&(GRAPH_MAX-1); + + float v0 = g->values[i0]; + float v1 = g->values[i1]; + + gfx_setcolor(0, 1, 0, 1); + gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); + } + gfx_lines_end(); +} + +typedef struct +{ + int64 snap; + int64 current; + int64 target; + + int64 rlast; + int64 tlast; + GRAPH graph; +} SMOOTHTIME; + +static void st_init(SMOOTHTIME *st, int64 target) +{ + st->snap = time_get(); + st->current = target; + st->target = target; + graph_init(&st->graph, 0.0f, 1.0f); +} + +static int64 st_get(SMOOTHTIME *st, int64 now) +{ + float adjust_speed, a; + int64 c = st->current + (now - st->snap); + int64 t = st->target + (now - st->snap); + int64 r; + + /* it's faster to adjust upward instead of downward */ + /* we might need to adjust these abit */ + adjust_speed = 0.25f; /*0.99f;*/ + if(t > c) + adjust_speed = 500.0f; + + a = ((now-st->snap)/(float)time_freq()) * adjust_speed; + if(a > 1.0f) + a = 1.0f; + + r = c + (int64)((t-c)*a); + + graph_add(&st->graph, a+0.5f); + + return r; +} + +static void st_update(SMOOTHTIME *st, int64 target) +{ + int64 now = time_get(); + st->current = st_get(st, now); + st->snap = now; + st->target = target; +} + +SMOOTHTIME game_time; +SMOOTHTIME predicted_time; + +GRAPH intra_graph; +GRAPH predict_graph; + +/* --- input snapping --- */ +static int input_data[MAX_INPUT_SIZE] = {0}; +static int input_data_size; +static int input_is_changed = 1; +static GRAPH input_late_graph; +void snap_input(void *data, int size) +{ + if(input_data_size != size || memcmp(input_data, data, size)) + input_is_changed = 1; + mem_copy(input_data, data, size); + input_data_size = size; +} + +/* -- snapshot handling --- */ +enum +{ + NUM_SNAPSHOT_TYPES=2 +}; + +SNAPSTORAGE snapshot_storage; +static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES]; +static int recived_snapshots; +static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; + +/* --- */ + +const void *snap_get_item(int snapid, int index, SNAP_ITEM *item) +{ + SNAPSHOT_ITEM *i; + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + i = snapshot_get_item(snapshots[snapid]->snap, index); + item->type = snapitem_type(i); + item->id = snapitem_id(i); + return (void *)snapitem_data(i); +} + +const void *snap_find_item(int snapid, int type, int id) +{ + /* TODO: linear search. should be fixed. */ + int i; + for(i = 0; i < snapshots[snapid]->snap->num_items; i++) + { + SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->snap, i); + if(snapitem_type(itm) == type && snapitem_id(itm) == id) + return (void *)snapitem_data(itm); + } + return 0x0; +} + +int snap_num_items(int snapid) +{ + dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); + return snapshots[snapid]->snap->num_items; +} + +/* ------ time functions ------ */ +float client_intratick() { return intratick; } +float client_intrapredtick() { return intrapredtick; } +int client_tick() { return current_tick; } +int client_predtick() { return current_predtick; } +int client_tickspeed() { return SERVER_TICK_SPEED; } +float client_frametime() { return frametime; } +float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); } + +/* ----- send functions ----- */ +int client_send_msg() +{ + const MSG_INFO *info = msg_get_info(); + NETPACKET packet; + mem_zero(&packet, sizeof(NETPACKET)); + + packet.client_id = 0; + packet.data = info->data; + packet.data_size = info->size; + + if(info->flags&MSGFLAG_VITAL) + packet.flags = PACKETFLAG_VITAL; + + netclient_send(net, &packet); + return 0; +} + +static void client_send_info() +{ + msg_pack_start_system(NETMSG_INFO, MSGFLAG_VITAL); + msg_pack_string(modc_net_version(), 128); + msg_pack_string(config.player_name, 128); + msg_pack_string(config.clan_name, 128); + msg_pack_string(config.password, 128); + msg_pack_end(); + client_send_msg(); +} + + +static void client_send_entergame() +{ + msg_pack_start_system(NETMSG_ENTERGAME, MSGFLAG_VITAL); + msg_pack_end(); + client_send_msg(); +} + +static void client_send_ready() +{ + msg_pack_start_system(NETMSG_READY, MSGFLAG_VITAL); + msg_pack_end(); + client_send_msg(); +} + +void client_rcon(const char *cmd) +{ + msg_pack_start_system(NETMSG_CMD, MSGFLAG_VITAL); + msg_pack_string(config.rcon_password, 32); + msg_pack_string(cmd, 256); + msg_pack_end(); + client_send_msg(); +} + +int client_connection_problems() +{ + return netclient_gotproblems(net); +} + +static void client_send_input() +{ + int64 now = time_get(); + int i; + + if(current_predtick <= 0) + return; + + msg_pack_start_system(NETMSG_INPUT, 0); + msg_pack_int(ack_game_tick); + msg_pack_int(current_predtick); + msg_pack_int(input_data_size); + + inputs[current_input].tick = current_predtick; + inputs[current_input].game_time = st_get(&predicted_time, now); + inputs[current_input].time = now; + + for(i = 0; i < input_data_size/4; i++) + { + inputs[current_input].data[i] = input_data[i]; + msg_pack_int(input_data[i]); + } + + current_input++; + current_input%=200; + + msg_pack_end(); + client_send_msg(); +} + +/* TODO: OPT: do this alot smarter! */ +int *client_get_input(int tick) +{ + int i; + int best = -1; + for(i = 0; i < 200; i++) + { + if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick)) + best = i; + } + + if(best != -1) + return (int *)inputs[best].data; + return 0; +} + +/* ------ state handling ----- */ +static int state = CLIENTSTATE_OFFLINE; +int client_state() { return state; } +static void client_set_state(int s) +{ + int old = state; + if(config.debug) + dbg_msg("client", "state change. last=%d current=%d", state, s); + state = s; + if(old != s) + modc_statechange(state, old); +} + +/* called when the map is loaded and we should init for a new round */ +static void client_on_enter_game() +{ + /* reset input */ + int i; + for(i = 0; i < 200; i++) + inputs[i].tick = -1; + current_input = 0; + + /* reset snapshots */ + snapshots[SNAP_CURRENT] = 0; + snapshots[SNAP_PREV] = 0; + snapstorage_purge_all(&snapshot_storage); + recived_snapshots = 0; + current_predtick = 0; + current_recv_tick = 0; +} + +void client_entergame() +{ + /* now we will wait for two snapshots */ + /* to finish the connection */ + client_send_entergame(); + client_on_enter_game(); +} + +void client_connect(const char *server_address_str) +{ + char buf[512]; + const char *port_str = 0; + int k; + int port = 8303; + + dbg_msg("client", "connecting to '%s'", server_address_str); + + strncpy(buf, server_address_str, 512); + + for(k = 0; buf[k]; k++) + { + if(buf[k] == ':') + { + port_str = &(buf[k+1]); + buf[k] = 0; + break; + } + } + + if(port_str) + port = atoi(port_str); + + if(net_host_lookup(buf, port, &server_address) != 0) + dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); + + netclient_connect(net, &server_address); + client_set_state(CLIENTSTATE_CONNECTING); + + graph_init(&intra_graph, 0.0f, 1.0f); + graph_init(&input_late_graph, 0.0f, 1.0f); + graph_init(&predict_graph, 0.0f, 200.0f); + +} + +void client_disconnect_with_reason(const char *reason) +{ + netclient_disconnect(net, reason); + client_set_state(CLIENTSTATE_OFFLINE); + map_unload(); +} + +void client_disconnect() +{ + netclient_disconnect(net, 0); + client_set_state(CLIENTSTATE_OFFLINE); + map_unload(); +} + +static int client_load_data() +{ + debug_font = gfx_load_texture("data/debug_font.png"); + return 1; +} + +extern int snapshot_data_rate[0xffff]; +extern int snapshot_data_updates[0xffff]; + +static void client_debug_render() +{ + static NETSTATS prev, current; + static int64 last_snap = 0; + static float frametime_avg = 0; + char buffer[512]; + + if(!config.debug) + return; + + gfx_blend_normal(); + gfx_texture_set(debug_font); + gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); + + if(time_get()-last_snap > time_freq()/10) + { + last_snap = time_get(); + prev = current; + netclient_stats(net, ¤t); + } + + frametime_avg = frametime_avg*0.9f + frametime*0.1f; + sprintf(buffer, "ticks: %8d %8d send: %6d recv: %6d snaploss: %d mem %dk gfxmem: %dk fps: %3d", + current_tick, current_predtick, + (current.send_bytes-prev.send_bytes)*10, + (current.recv_bytes-prev.recv_bytes)*10, + snaploss, + mem_allocated()/1024, + gfx_memory_usage()/1024, + (int)(1.0f/frametime_avg)); + gfx_quads_text(2, 2, 16, buffer); + + sprintf(buffer, "ui: %p %p", ui_hot_item(), ui_active_item()); + gfx_quads_text(2, 16, 16, buffer); + + + /* render rates */ + { + int i; + for(i = 0; i < 256; i++) + { + if(snapshot_data_rate[i]) + { + sprintf(buffer, "%4d : %8d %8d %8d", i, snapshot_data_rate[i]/8, snapshot_data_updates[i], + (snapshot_data_rate[i]/snapshot_data_updates[i])/8); + gfx_quads_text(2, 100+i*8, 16, buffer); + } + } + } + + /* render graphs */ + gfx_mapscreen(0,0,400.0f,300.0f); + graph_render(&predict_graph, 300, 10, 90, 50); + graph_render(&predicted_time.graph, 300, 10+50+10, 90, 50); + + graph_render(&intra_graph, 300, 10+50+10+50+10, 90, 50); + graph_render(&input_late_graph, 300, 10+50+10+50+10+50+10, 90, 50); + +} + +void client_quit() +{ + client_set_state(CLIENTSTATE_QUITING); +} + +const char *client_error_string() +{ + return netclient_error_string(net); +} + +static void client_render() +{ + gfx_clear(0.0f,0.0f,0.0f); + modc_render(); + client_debug_render(); +} + +static const char *client_load_map(const char *mapname, int wanted_crc) +{ + static char errormsg[128]; + DATAFILE *df; + char buf[512]; + int crc; + + dbg_msg("client", "loading map, map=%s wanted crc=%08x", mapname, wanted_crc); + client_set_state(CLIENTSTATE_LOADING); + + sprintf(buf, "data/maps/%s.map", mapname); + df = datafile_load(buf); + if(!df) + { + sprintf(errormsg, "map '%s' not found", mapname); + return errormsg; + } + + /* get the crc of the map */ + crc = datafile_crc(buf); + if(crc != wanted_crc) + { + datafile_unload(df); + sprintf(errormsg, "map differs from the server. %08x != %08x", crc, wanted_crc); + return errormsg; + } + + map_set(df); + return NULL; +} + +static void client_process_packet(NETPACKET *packet) +{ + if(packet->client_id == -1) + { + /* connectionlesss */ + if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && + memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) + { + int size = packet->data_size-sizeof(SERVERBROWSE_LIST); + int num = size/sizeof(NETADDR4); + NETADDR4 *addrs = (NETADDR4 *)((char*)packet->data+sizeof(SERVERBROWSE_LIST)); + int i; + + for(i = 0; i < num; i++) + { + NETADDR4 addr = addrs[i]; + SERVER_INFO info = {0}; + +#if defined(CONF_ARCH_ENDIAN_BIG) + const char *tmp = (const char *)&addr.port; + addr.port = (tmp[1]<<8) | tmp[0]; +#endif + + info.latency = 999; + sprintf(info.address, "%d.%d.%d.%d:%d", + addr.ip[0], addr.ip[1], addr.ip[2], + addr.ip[3], addr.port); + sprintf(info.name, "\255%d.%d.%d.%d:%d", /* the \255 is to make sure that it's sorted last */ + addr.ip[0], addr.ip[1], addr.ip[2], + addr.ip[3], addr.port); + + client_serverbrowse_set(addrs+i, 1, &info); + } + } + + if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && + memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) + { + /* we got ze info */ + UNPACKER up; + SERVER_INFO info = {0}; + int i; + + unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); + + strncpy(info.version, unpacker_get_string(&up), 32); + strncpy(info.name, unpacker_get_string(&up), 64); + strncpy(info.map, unpacker_get_string(&up), 32); + info.game_type = atol(unpacker_get_string(&up)); + info.flags = atol(unpacker_get_string(&up)); + info.progression = atol(unpacker_get_string(&up)); + info.num_players = atol(unpacker_get_string(&up)); + info.max_players = atol(unpacker_get_string(&up)); + sprintf(info.address, "%d.%d.%d.%d:%d", + packet->address.ip[0], packet->address.ip[1], packet->address.ip[2], + packet->address.ip[3], packet->address.port); + + for(i = 0; i < info.num_players; i++) + { + strncpy(info.player_names[i], unpacker_get_string(&up), 48); + info.player_scores[i] = atol(unpacker_get_string(&up)); + } + + /* TODO: unpack players aswell */ + client_serverbrowse_set(&packet->address, 0, &info); + } + } + else + { + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + + if(sys) + { + /* system message */ + if(msg == NETMSG_MAP) + { + const char *map = msg_unpack_string(); + int map_crc = msg_unpack_int(); + const char *error = client_load_map(map, map_crc); + + if(!error) + { + dbg_msg("client/network", "loading done"); + client_send_ready(); + modc_connected(); + } + else + { + client_disconnect_with_reason(error); + } + } + else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY) + { + /*dbg_msg("client/network", "got snapshot"); */ + int num_parts = 1; + int part = 0; + int game_tick = msg_unpack_int(); + int delta_tick = game_tick-msg_unpack_int(); + int input_predtick = msg_unpack_int(); + int time_left = msg_unpack_int(); + int part_size = 0; + int crc = 0; + int complete_size = 0; + + if(msg == NETMSG_SNAP) + { + num_parts = msg_unpack_int(); + part = msg_unpack_int(); + } + + if(msg != NETMSG_SNAPEMPTY) + { + crc = msg_unpack_int(); + part_size = msg_unpack_int(); + } + + /* TODO: adjust our prediction time */ + if(time_left) + { + int k; + + graph_add(&input_late_graph, time_left/100.0f+0.5f); + + if(time_left < 0) + dbg_msg("client", "input was late with %d ms", time_left); + + for(k = 0; k < 200; k++) /* TODO: do this better */ + { + if(inputs[k].tick == input_predtick) + { + /*-1000/50 prediction_margin */ + int64 target = inputs[k].game_time + (time_get() - inputs[k].time); + st_update(&predicted_time, target - (int64)(((time_left-prediction_margin)/1000.0f)*time_freq())); + break; + } + } + } + + if(snapshot_part == part && game_tick > current_recv_tick) + { + /* TODO: clean this up abit */ + const char *d = (const char *)msg_unpack_raw(part_size); + mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); + snapshot_part++; + + if(snapshot_part == num_parts) + { + static SNAPSHOT emptysnap; + SNAPSHOT *deltashot = &emptysnap; + int purgetick; + void *deltadata; + int deltasize; + unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; + int snapsize; + + complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size; + + snapshot_part = 0; + + /* find snapshot that we should use as delta */ + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + /* find delta */ + if(delta_tick >= 0) + { + int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot); + + if(deltashot_size < 0) + { + /* 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 */ + /* TODO: combine this with the input message */ + ack_game_tick = -1; + return; + } + } + + /* decompress snapshot */ + deltadata = snapshot_empty_delta(); + deltasize = sizeof(int)*3; + + if(complete_size) + { + int compsize = zerobit_decompress(snapshot_incomming_data, complete_size, tmpbuffer); + int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); + deltadata = tmpbuffer2; + deltasize = intsize; + } + + /*dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick); */ + + purgetick = delta_tick; + snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize); + if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc) + { + if(config.debug) + dbg_msg("client", "snapshot crc error %d", snapcrcerrors); + snapcrcerrors++; + if(snapcrcerrors > 10) + { + /* to many errors, send reset */ + ack_game_tick = -1; + client_send_input(); + snapcrcerrors = 0; + } + return; + } + else + { + if(snapcrcerrors) + snapcrcerrors--; + } + + /* purge old snapshots */ + purgetick = delta_tick; + if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->tick < purgetick) + purgetick = snapshots[SNAP_PREV]->tick; + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->tick < purgetick) + purgetick = snapshots[SNAP_PREV]->tick; + snapstorage_purge_until(&snapshot_storage, purgetick); + /*client_snapshot_purge_until(game_tick-50); */ + + /* add new */ + snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3); + + /* apply snapshot, cycle pointers */ + recived_snapshots++; + + + if(current_recv_tick > 0) + snaploss += game_tick-current_recv_tick-1; + current_recv_tick = game_tick; + + /* we got two snapshots until we see us self as connected */ + if(recived_snapshots == 2) + { + /* start at 200ms and work from there */ + st_init(&predicted_time, game_tick*time_freq()/50); + st_init(&game_time, (game_tick-1)*time_freq()/50); + snapshots[SNAP_PREV] = snapshot_storage.first; + snapshots[SNAP_CURRENT] = snapshot_storage.last; + local_start_time = time_get(); + client_set_state(CLIENTSTATE_ONLINE); + } + + { + int64 now = time_get(); + graph_add(&predict_graph, (st_get(&predicted_time, now)-st_get(&game_time, now))/(float)time_freq()); + } + + st_update(&game_time, (game_tick-1)*time_freq()/50); + + /* ack snapshot */ + ack_game_tick = game_tick; + } + } + else + { + dbg_msg("client", "snapsht reset!"); + snapshot_part = 0; + } + } + } + else + { + /* game message */ + modc_message(msg); + } + } +} + +static void client_pump_network() +{ + NETPACKET packet; + + netclient_update(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(); + } + + /* process packets */ + while(netclient_recv(net, &packet)) + client_process_packet(&packet); +} + +static void client_update() +{ + /* switch snapshot */ + if(client_state() != CLIENTSTATE_OFFLINE && recived_snapshots >= 3) + { + int repredict = 0; + int64 now = st_get(&game_time, time_get()); + int64 pred_now = st_get(&predicted_time, time_get()); + + while(1) + { + SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT]; + int64 tickstart = (cur->tick)*time_freq()/50; + + if(tickstart < now) + { + SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next; + if(next) + { + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = next; + + /* set tick */ + current_tick = snapshots[SNAP_CURRENT]->tick; + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + modc_newsnapshot(); + repredict = 1; + } + } + else + break; + } + else + break; + } + + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) + { + int64 curtick_start = (snapshots[SNAP_CURRENT]->tick)*time_freq()/50; + int64 prevtick_start = (snapshots[SNAP_PREV]->tick)*time_freq()/50; + /*tg_add(&predicted_time_graph, pred_now, 0); */ + int prev_pred_tick = (int)(pred_now*50/time_freq()); + int new_pred_tick = prev_pred_tick+1; + static float last_intrapred = 0; + + intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start); + + graph_add(&intra_graph, intratick*0.25f); + + curtick_start = new_pred_tick*time_freq()/50; + prevtick_start = prev_pred_tick*time_freq()/50; + intrapredtick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); + + + if(new_pred_tick > current_predtick) + { + last_intrapred = intrapredtick; + current_predtick = new_pred_tick; + repredict = 1; + + /* send input */ + client_send_input(); + } + + if(intrapredtick < last_intrapred) + dbg_msg("client", "prediction time goes backwards, that can't be good"); + last_intrapred = intrapredtick; + } + + /* only do sane predictions */ + if(repredict) + { + if(current_predtick > current_tick && current_predtick < current_tick+50) + modc_predict(); + } + } + + /* STRESS TEST: join the server again */ + if(client_state() == CLIENTSTATE_OFFLINE && config.dbg_stress && (frames%100) == 0) + client_connect(config.dbg_stress_server); + + /* pump the network */ + client_pump_network(); + + /* update the server browser */ + client_serverbrowse_update(); +} + +extern int editor_update_and_render(); +extern void editor_init(); + +static void client_run() +{ + NETADDR4 bindaddr; + int64 reporttime = time_get(); + int64 reportinterval = time_freq()*1; + int editor_active = 0; + + local_start_time = time_get(); + snapshot_part = 0; + + /* init graphics and sound */ + if(!gfx_init()) + return; + + /* init the editor */ + editor_init(); + + /* sound is allowed to fail */ + snd_init(); + + /* load data */ + if(!client_load_data()) + return; + + /* init the mod */ + modc_init(); + dbg_msg("client", "version %s", modc_net_version()); + + /* open socket */ + mem_zero(&bindaddr, sizeof(bindaddr)); + net = netclient_open(bindaddr, 0); + + /* connect to the server if wanted */ + /* + if(config.cl_connect[0] != 0) + client_connect(config.cl_connect); + config.cl_connect[0] = 0; + */ + + inp_mouse_mode_relative(); + + while (1) + { + int64 frame_start_time = time_get(); + frames++; + + /* update input */ + inp_update(); + + /* refocus */ + if(!gfx_window_active()) + { + if(window_must_refocus == 0) + inp_mouse_mode_absolute(); + window_must_refocus = 1; + } + + if(window_must_refocus && gfx_window_active()) + { + if(window_must_refocus < 3) + { + inp_mouse_mode_absolute(); + window_must_refocus++; + } + + if(inp_key_pressed(KEY_MOUSE_1)) + { + inp_mouse_mode_relative(); + window_must_refocus = 0; + } + } + + /* screenshot button */ + if(inp_key_down(config.key_screenshot)) + gfx_screenshot(); + + /* some debug keys */ + /* + if(config.debug) + { + if(inp_key_pressed(KEY_F1)) + inp_mouse_mode_absolute(); + if(inp_key_pressed(KEY_F2)) + inp_mouse_mode_relative(); + + if(inp_key_pressed(KEY_F5)) + { + ack_game_tick = -1; + client_send_input(); + } + }*/ + + /* panic quit button */ + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_pressed('Q')) + break; + + if(inp_key_pressed(KEY_LCTRL) && inp_key_pressed(KEY_LSHIFT) && inp_key_down('E')) + editor_active = editor_active^1; + + if(!gfx_window_open()) + break; + + /* render */ + if(editor_active) + { + client_update(); + editor_update_and_render(); + gfx_swap(); + } + else + { + client_update(); + + if(config.dbg_stress) + { + if((frames%10) == 0) + { + client_render(); + gfx_swap(); + } + } + else + { + client_render(); + gfx_swap(); + } + } + + /* check conditions */ + if(client_state() == CLIENTSTATE_QUITING) + break; + + /* be nice */ + if(config.cl_cpu_throttle || !gfx_window_active()) + thread_sleep(1); + + if(reporttime < time_get()) + { + if(config.debug) + { + dbg_msg("client/report", "fps=%.02f (%.02f %.02f) netstate=%d", + frames/(float)(reportinterval/time_freq()), + 1.0f/frametime_high, + 1.0f/frametime_low, + netclient_state(net)); + } + frametime_low = 1; + frametime_high = 0; + frames = 0; + reporttime += reportinterval; + } + + /* update frametime */ + frametime = (time_get()-frame_start_time)/(float)time_freq(); + if(frametime < frametime_low) + frametime_low = frametime; + if(frametime > frametime_high) + frametime_high = frametime; + } + + modc_shutdown(); + client_disconnect(); + + gfx_shutdown(); + snd_shutdown(); +} + + +int editor_main(int argc, char **argv); + +int main(int argc, char **argv) +{ + /* init the engine */ + dbg_msg("client", "starting..."); + engine_init("Teewars", argc, argv); + + client_run(); + + engine_writeconfig(); + return 0; +} diff --git a/src/engine/client/ec_gfx.c b/src/engine/client/ec_gfx.c new file mode 100644 index 00000000..cd1f690b --- /dev/null +++ b/src/engine/client/ec_gfx.c @@ -0,0 +1,1018 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* compressed textures */ +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE + +enum +{ + DRAWING_QUADS=1, + DRAWING_LINES=2 +}; + +/* */ +typedef struct { float x, y, z; } VEC3; +typedef struct { float u, v; } TEXCOORD; +typedef struct { float r, g, b, a; } COLOR; + +typedef struct +{ + VEC3 pos; + TEXCOORD tex; + COLOR color; +} VERTEX; + +const int vertex_buffer_size = 32*1024; +static VERTEX *vertices = 0; +static int num_vertices = 0; + +static COLOR color[4]; +static TEXCOORD texture[4]; + +static int do_screenshot = 0; + +static int screen_width = -1; +static int screen_height = -1; +static float rotation = 0; +static int drawing = 0; + +static float screen_x0 = 0; +static float screen_y0 = 0; +static float screen_x1 = 0; +static float screen_y1 = 0; + +typedef struct +{ + GLuint tex; + int memsize; + int flags; + int next; +} TEXTURE; + + +enum +{ + MAX_TEXTURES = 128 +}; + +static TEXTURE textures[MAX_TEXTURES]; +static int first_free_texture; +static int memory_usage = 0; + +static const unsigned char null_texture_data[] = { + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, + 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, +}; + + +typedef struct +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +} pretty_font; + +pretty_font default_font = +{ +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, + 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, + 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, + 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, + 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, + 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, + 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, + 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, + 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, + 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, + 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, + 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, +}, +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, + 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, + 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, + 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, + 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, + 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, + 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, + 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, + 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, + 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, + 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, +}, +0 +}; + +double extra_kerning[256*256] = {0}; + +pretty_font *current_font = &default_font; + +static void flush() +{ + if(num_vertices == 0) + return; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glVertexPointer(3, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, + sizeof(VERTEX), + (char*)vertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + if(drawing == DRAWING_QUADS) + glDrawArrays(GL_QUADS, 0, num_vertices); + else if(drawing == DRAWING_LINES) + glDrawArrays(GL_LINES, 0, num_vertices); + + /* Reset pointer */ + num_vertices = 0; +} + +static void draw_line() +{ + num_vertices += 2; + if((num_vertices + 2) >= vertex_buffer_size) + flush(); +} + +static void draw_quad() +{ + num_vertices += 4; + if((num_vertices + 4) >= vertex_buffer_size) + flush(); +} + + +int gfx_init() +{ + int i; + + screen_width = config.gfx_screen_width; + screen_height = config.gfx_screen_height; + + glfwInit(); + + if(config.dbg_stress) + { + screen_width = 320; + screen_height = 240; + } + + /* set antialiasing */ + if(config.gfx_fsaa_samples) + glfwOpenWindowHint(GLFW_FSAA_SAMPLES, config.gfx_fsaa_samples); + + /* set refresh rate */ + if(config.gfx_refresh_rate) + glfwOpenWindowHint(GLFW_REFRESH_RATE, config.gfx_refresh_rate); + + /* no resizing allowed */ + glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, 1); + + /* open window */ + if(config.gfx_fullscreen) + { + int result = glfwOpenWindow(screen_width, screen_height, 8, 8, 8, 0, 24, 0, GLFW_FULLSCREEN); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + else + { + int result = glfwOpenWindow(screen_width, screen_height, 0, 0, 0, 0, 24, 0, GLFW_WINDOW); + if(result != GL_TRUE) + { + dbg_msg("game", "failed to create gl context"); + return 0; + } + } + + glGetIntegerv(GL_DEPTH_BITS, &i); + dbg_msg("gfx", "depthbits = %d", i); + + glfwSetWindowTitle("Teewars"); + + /* We don't want to see the window when we run the stress testing */ + if(config.dbg_stress) + glfwIconifyWindow(); + + /* Init vertices */ + if (vertices) + mem_free(vertices); + vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1); + num_vertices = 0; + + + /* + dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), + context.version_minor(), + context.version_rev());*/ + + gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); + + /* set some default settings */ + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + gfx_mask_op(MASK_NONE, 0); + + /* Set all z to -5.0f */ + for (i = 0; i < vertex_buffer_size; i++) + vertices[i].pos.z = -5.0f; + + /* init textures */ + first_free_texture = 0; + for(i = 0; i < MAX_TEXTURES; i++) + textures[i].next = i+1; + textures[MAX_TEXTURES-1].next = -1; + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + /* init input */ + inp_init(); + + /* create null texture, will get id=0 */ + gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data); + + /* set vsync as needed */ + gfx_set_vsync(config.gfx_vsync); + + /* UGLY as hell, please remove */ + current_font->font_texture = gfx_load_texture("data/big_font.png"); + + return 1; +} + +float gfx_screenaspect() +{ + return gfx_screenwidth()/(float)gfx_screenheight(); +} + +void gfx_clear_mask(int fill) +{ + /*if(fill) + glClearDepth(0.0f); + else*/ + + int i; + glGetIntegerv(GL_DEPTH_WRITEMASK, &i); + glDepthMask(GL_TRUE); + glClearDepth(0.0f); + glClear(GL_DEPTH_BUFFER_BIT); + glDepthMask(i); +} + +void gfx_mask_op(int mask, int write) +{ + glEnable(GL_DEPTH_TEST); + + if(write) + glDepthMask(GL_TRUE); + else + glDepthMask(GL_FALSE); + + if(mask == MASK_NONE) + glDepthFunc(GL_ALWAYS); + else if(mask == MASK_SET) + glDepthFunc(GL_LEQUAL); + else if(mask == MASK_ZERO) + glDepthFunc(GL_NOTEQUAL); +} + +int gfx_window_active() +{ + return glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE ? 1 : 0; +} + +int gfx_window_open() +{ + return glfwGetWindowParam(GLFW_OPENED) == GL_TRUE ? 1 : 0; +} + +VIDEO_MODE fakemodes[] = { + {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, + {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, + {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, + {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, + {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, + {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, + {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, + {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, + {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, + {2048,1536,8,8,8}, + + {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, + {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, + {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, + {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, + {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, + {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, + {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, + {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, + {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, + {2048,1536,5,6,5} +}; + +int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) +{ + if(config.gfx_display_all_modes) + { + int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); + mem_copy(list, fakemodes, sizeof(fakemodes)); + if(maxcount < count) + count = maxcount; + return count; + } + + return glfwGetVideoModes((GLFWvidmode *)list, maxcount); +} + +void gfx_set_vsync(int val) +{ + glfwSwapInterval(val); +} + +int gfx_unload_texture(int index) +{ + glDeleteTextures(1, &textures[index].tex); + textures[index].next = first_free_texture; + memory_usage -= textures[index].memsize; + first_free_texture = index; + return 0; +} + +void gfx_blend_normal() +{ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void gfx_blend_additive() +{ + glBlendFunc(GL_SRC_ALPHA, GL_ONE); +} + +int gfx_memory_usage() { return memory_usage; } + +static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) +{ + return (data[(v*w+u)*4+offset]+ + data[(v*w+u+1)*4+offset]+ + data[((v+1)*w+u)*4+offset]+ + data[((v+1)*w+u+1)*4+offset])/4; +} + +int gfx_load_texture_raw(int w, int h, int format, const void *data) +{ + int mipmap = 1; + unsigned char *texdata = (unsigned char *)data; + unsigned char *tmpdata = 0; + int oglformat = 0; + int tex = 0; + + /* don't waste memory on texture if we are stress testing */ + if(config.dbg_stress) + return -1; + + /* grab texture */ + tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + /* resample if needed */ + if(config.gfx_texture_quality==0) + { + if(w > 16 && h > 16 && format == IMG_RGBA) + { + unsigned char *tmpdata; + int c = 0; + int x, y; + + tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); + + w/=2; + h/=2; + + for(y = 0; y < h; y++) + for(x = 0; x < w; x++, c++) + { + tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); + tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); + tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); + tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); + } + texdata = tmpdata; + } + } + + if(config.debug) + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + + /* upload texture */ + if(config.gfx_texture_compression) + { + oglformat = GL_COMPRESSED_RGBA_ARB; + if(format == IMG_RGB) + oglformat = GL_COMPRESSED_RGB_ARB; + } + else + { + oglformat = GL_RGBA; + if(format == IMG_RGB) + oglformat = GL_RGB; + } + + glGenTextures(1, &textures[tex].tex); + glBindTexture(GL_TEXTURE_2D, textures[tex].tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + gluBuild2DMipmaps(GL_TEXTURE_2D, oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); + + /* calculate memory usage */ + textures[tex].memsize = w*h*4; + if(mipmap) + { + while(w > 2 && h > 2) + { + w>>=1; + h>>=1; + textures[tex].memsize += w*h*4; + } + } + + memory_usage += textures[tex].memsize; + mem_free(tmpdata); + return tex; +} +/* +int gfx_load_mip_texture_raw(int w, int h, int format, const void *data) +{ + // grab texture + int tex = first_free_texture; + first_free_texture = textures[tex].next; + textures[tex].next = -1; + + // set data and return + // TODO: should be RGBA, not BGRA + dbg_msg("gfx", "%d = %dx%d", tex, w, h); + dbg_assert(format == IMG_RGBA, "not an RGBA image"); + + unsigned mip_w = w; + unsigned mip_h = h; + unsigned level = 0; + const unsigned char *ptr = (const unsigned char*)data; + while(mip_w > 0 && mip_h > 0) + { + dbg_msg("gfx mip", "%d = %dx%d", level, mip_w, mip_h); + textures[tex].tex.data2d_mip(mip_w, mip_h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, level, ptr); + level++; + ptr = ptr + mip_w*mip_h*4; + mip_w = mip_w>>1; + mip_h = mip_h>>1; + } + return tex; +} +*/ + +/* simple uncompressed RGBA loaders */ +int gfx_load_texture(const char *filename) +{ + int l = strlen(filename); + IMAGE_INFO img; + if(l < 3) + return 0; + if(gfx_load_png(&img, filename)) + { + int id = gfx_load_texture_raw(img.width, img.height, img.format, img.data); + mem_free(img.data); + return id; + } + + return 0; +} + +int gfx_load_png(IMAGE_INFO *img, const char *filename) +{ + unsigned char *buffer; + png_t png; + + /* open file for reading */ + png_init(0,0); + + if(png_open_file(&png, filename) != PNG_NO_ERROR) + { + dbg_msg("game/png", "failed to open file. filename='%s'", filename); + return 0; + } + + if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) + { + dbg_msg("game/png", "invalid format. filename='%s'", filename); + png_close_file(&png); + } + + buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); + png_get_data(&png, buffer); + png_close_file(&png); + + img->width = png.width; + img->height = png.height; + if(png.color_type == PNG_TRUECOLOR) + img->format = IMG_RGB; + else if(png.color_type == PNG_TRUECOLOR_ALPHA) + img->format = IMG_RGBA; + img->data = buffer; + return 1; +} + +void gfx_shutdown() +{ + if (vertices) + mem_free(vertices); + glfwCloseWindow(); + glfwTerminate(); +} + +void gfx_screenshot() +{ + do_screenshot = 1; +} + +void gfx_swap() +{ + if(do_screenshot) + { + /* fetch image data */ + int y; + int w = screen_width; + int h = screen_height; + unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1); + unsigned char *temp_row = pixel_data+w*h*3; + glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); + + /* flip the pixel because opengl works from bottom left corner */ + for(y = 0; y < h/2; y++) + { + mem_copy(temp_row, pixel_data+y*w*3, w*3); + mem_copy(pixel_data+y*w*3, pixel_data+(h-y-1)*w*3, w*3); + mem_copy(pixel_data+(h-y-1)*w*3, temp_row,w*3); + } + + /* find filename */ + { + char wholepath[1024]; + char filename[128]; + static int index = 1; + png_t png; + + for(; index < 1000; index++) + { + IOHANDLE io; + sprintf(filename, "screenshots/screenshot%04d.png", index); + engine_savepath(filename, wholepath, sizeof(wholepath)); + + io = io_open(wholepath, IOFLAG_READ); + if(io) + io_close(io); + else + break; + } + + /* save png */ + dbg_msg("client", "saved screenshot to '%s'", wholepath); + png_open_file_write(&png, wholepath); + png_set_data(&png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pixel_data); + png_close_file(&png); + } + + /* clean up */ + mem_free(pixel_data); + do_screenshot = 0; + } + + glfwSwapBuffers(); + glFinish(); + glfwPollEvents(); +} + +int gfx_screenwidth() +{ + return screen_width; +} + +int gfx_screenheight() +{ + return screen_height; +} + +void gfx_texture_set(int slot) +{ + dbg_assert(drawing == 0, "called gfx_texture_set within begin"); + if(slot == -1) + glDisable(GL_TEXTURE_2D); + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, textures[slot].tex); + } +} + +void gfx_clear(float r, float g, float b) +{ + glClearColor(r,g,b,1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) +{ + screen_x0 = tl_x; + screen_y0 = tl_y; + screen_x1 = br_x; + screen_y1 = br_y; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); +} + +void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) +{ + *tl_x = screen_x0; + *tl_y = screen_y0; + *br_x = screen_x1; + *br_y = screen_y1; +} + +void gfx_setoffset(float x, float y) +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(x, y, 0); +} + + +void gfx_quads_begin() +{ + dbg_assert(drawing == 0, "called quads_begin twice"); + drawing = DRAWING_QUADS; + + gfx_quads_setsubset(0,0,1,1); + gfx_quads_setrotation(0); + gfx_setcolor(1,1,1,1); +} + +void gfx_quads_end() +{ + dbg_assert(drawing == DRAWING_QUADS, "called quads_end without begin"); + flush(); + drawing = 0; +} + + +void gfx_quads_setrotation(float angle) +{ + dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setrotation without begin"); + rotation = angle; +} + +void gfx_setcolorvertex(int i, float r, float g, float b, float a) +{ + dbg_assert(drawing != 0, "called gfx_quads_setcolorvertex without begin"); + color[i].r = r; + color[i].g = g; + color[i].b = b; + color[i].a = a; +} + +void gfx_setcolor(float r, float g, float b, float a) +{ + dbg_assert(drawing != 0, "called gfx_quads_setcolor without begin"); + gfx_setcolorvertex(0, r, g, b, a); + gfx_setcolorvertex(1, r, g, b, a); + gfx_setcolorvertex(2, r, g, b, a); + gfx_setcolorvertex(3, r, g, b, a); +} + +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) +{ + dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setsubset without begin"); + + texture[0].u = tl_u; + texture[0].v = tl_v; + + texture[1].u = br_u; + texture[1].v = tl_v; + + texture[2].u = br_u; + texture[2].v = br_v; + + texture[3].u = tl_u; + texture[3].v = br_v; +} + +static void rotate(VEC3 *center, VEC3 *point) +{ + float x = point->x - center->x; + float y = point->y - center->y; + point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; + point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; +} + +void gfx_quads_draw(float x, float y, float w, float h) +{ + gfx_quads_drawTL(x-w/2, y-h/2,w,h); +} + +void gfx_quads_drawTL(float x, float y, float width, float height) +{ + VEC3 center; + + dbg_assert(drawing == DRAWING_QUADS, "called quads_draw without begin"); + + center.x = x + width/2; + center.y = y + height/2; + center.z = 0; + + vertices[num_vertices].pos.x = x; + vertices[num_vertices].pos.y = y; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + rotate(¢er, &vertices[num_vertices].pos); + + vertices[num_vertices + 1].pos.x = x+width; + vertices[num_vertices + 1].pos.y = y; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + rotate(¢er, &vertices[num_vertices + 1].pos); + + vertices[num_vertices + 2].pos.x = x + width; + vertices[num_vertices + 2].pos.y = y+height; + vertices[num_vertices + 2].tex = texture[2]; + vertices[num_vertices + 2].color = color[2]; + rotate(¢er, &vertices[num_vertices + 2].pos); + + vertices[num_vertices + 3].pos.x = x; + vertices[num_vertices + 3].pos.y = y+height; + vertices[num_vertices + 3].tex = texture[3]; + vertices[num_vertices + 3].color = color[3]; + rotate(¢er, &vertices[num_vertices + 3].pos); + + draw_quad(); +} + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3) +{ + dbg_assert(drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); + + vertices[num_vertices].pos.x = x0; + vertices[num_vertices].pos.y = y0; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + + vertices[num_vertices + 1].pos.x = x1; + vertices[num_vertices + 1].pos.y = y1; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + + vertices[num_vertices + 2].pos.x = x3; + vertices[num_vertices + 2].pos.y = y3; + vertices[num_vertices + 2].tex = texture[3]; + vertices[num_vertices + 2].color = color[3]; + + vertices[num_vertices + 3].pos.x = x2; + vertices[num_vertices + 3].pos.y = y2; + vertices[num_vertices + 3].tex = texture[2]; + vertices[num_vertices + 3].color = color[2]; + + draw_quad(); +} + +void gfx_quads_text(float x, float y, float size, const char *text) +{ + float startx = x; + + gfx_quads_begin(); + + while(*text) + { + char c = *text; + text++; + + if(c == '\n') + { + x = startx; + y += size; + } + else + { + gfx_quads_setsubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + gfx_quads_drawTL(x,y,size,size); + x += size/2; + } + } + + gfx_quads_end(); +} + +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++; + } +} + + + +static float pretty_r=1; +static float pretty_g=1; +static float pretty_b=1; +static float pretty_a=1; + +void gfx_pretty_text_color(float r, float g, float b, float a) +{ + pretty_r = r; + pretty_g = g; + pretty_b = b; + pretty_a = a; +} + +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(); + gfx_setcolor(pretty_r, pretty_g, pretty_b, pretty_a); + + if(length < 0) + length = strlen(text_); + + while(length) + { + const int c = *text; + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + double x_nudge = 0; + + text++; + + 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); + + 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; +} + + + +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 unsigned char *stop; + + if (length == -1) + stop = text + strlen((char*)text); + else + stop = text + length; + + while (text < stop) + { + const int c = *text; + const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; + + double x_nudge = 0; + if (text[1]) + x_nudge = extra_kerning[text[0] + text[1] * 256]; + + w += (width + spacing + x_nudge) * size; + + text++; + } + + return w; +} + + + +void gfx_lines_begin() +{ + dbg_assert(drawing == 0, "called begin twice"); + drawing = DRAWING_LINES; + gfx_setcolor(1,1,1,1); +} + +void gfx_lines_end() +{ + dbg_assert(drawing == DRAWING_LINES, "called end without begin"); + flush(); + drawing = 0; +} + +void gfx_lines_draw(float x0, float y0, float x1, float y1) +{ + dbg_assert(drawing == DRAWING_LINES, "called draw without begin"); + + vertices[num_vertices].pos.x = x0; + vertices[num_vertices].pos.y = y0; + vertices[num_vertices].tex = texture[0]; + vertices[num_vertices].color = color[0]; + + vertices[num_vertices + 1].pos.x = x1; + vertices[num_vertices + 1].pos.y = y1; + vertices[num_vertices + 1].tex = texture[1]; + vertices[num_vertices + 1].color = color[1]; + + draw_line(); +} diff --git a/src/engine/client/ec_inp.c b/src/engine/client/ec_inp.c new file mode 100644 index 00000000..0366cff5 --- /dev/null +++ b/src/engine/client/ec_inp.c @@ -0,0 +1,210 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include +#include +#include + +static int keyboard_state[2][1024]; /* TODO: fix this!! */ +static int keyboard_current = 0; +static int keyboard_first = 1; + + +static struct +{ + unsigned char presses; + unsigned char releases; +} input_count[2][1024] = {{{0}}, {{0}}}; + +static unsigned char input_state[2][1024] = {{0}, {0}}; + +static int input_current = 0; +static unsigned int last_release = 0; +static unsigned int release_delta = -1; + +void inp_mouse_relative(int *x, int *y) +{ + static int last_x = 0, last_y = 0; + static int last_sens = 100.0f; + int nx, ny; + float sens = config.inp_mousesens/100.0f; + + if(last_sens != config.inp_mousesens) + { + last_x = (last_x/(float)last_sens)*(float)config.inp_mousesens; + last_y = (last_y/(float)last_sens)*(float)config.inp_mousesens; + last_sens = config.inp_mousesens; + } + + + glfwGetMousePos(&nx, &ny); + nx *= sens; + ny *= sens; + + *x = nx-last_x; + *y = ny-last_y; + last_x = nx; + last_y = ny; +} + +static char last_c = 0; +static int last_k = 0; + +static void char_callback(int character, int action) +{ + if(action == GLFW_PRESS && character < 256) + last_c = (char)character; +} + +static void key_callback(int key, int action) +{ + if(action == GLFW_PRESS) + last_k = key; + + if(action == GLFW_PRESS) + input_count[input_current^1][key].presses++; + if(action == GLFW_RELEASE) + input_count[input_current^1][key].releases++; + input_state[input_current^1][key] = action; +} + +static void mousebutton_callback(int button, int action) +{ + if(action == GLFW_PRESS) + last_k = KEY_MOUSE_FIRST+button; + + if(action == GLFW_PRESS) + input_count[input_current^1][KEY_MOUSE_FIRST+button].presses++; + if(action == GLFW_RELEASE) + { + if(button == 0) + { + release_delta = time_get() - last_release; + last_release = time_get(); + } + input_count[input_current^1][KEY_MOUSE_FIRST+button].releases++; + } + input_state[input_current^1][KEY_MOUSE_FIRST+button] = action; +} + + +static void mousewheel_callback(int pos) +{ + if(pos > 0) + { + while(pos-- != 0) + { + input_count[input_current^1][KEY_MOUSE_WHEEL_UP].presses++; + input_count[input_current^1][KEY_MOUSE_WHEEL_UP].releases++; + } + } + else if(pos < 0) + { + while(pos++ != 0) + { + input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].presses++; + input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].releases++; + } + } + glfwSetMouseWheel(0); +} + + +void inp_init() +{ + glfwEnable(GLFW_KEY_REPEAT); + glfwSetCharCallback(char_callback); + glfwSetKeyCallback(key_callback); + glfwSetMouseButtonCallback(mousebutton_callback); + glfwSetMouseWheelCallback(mousewheel_callback); +} + +char inp_last_char() +{ + return last_c; +} + +int inp_last_key() +{ + return last_k; +} + +void inp_clear() +{ + last_k = 0; + last_c = 0; +} + +void inp_mouse_mode_absolute() +{ + glfwEnable(GLFW_MOUSE_CURSOR); +} + +void inp_mouse_mode_relative() +{ + glfwDisable(GLFW_MOUSE_CURSOR); +} + +int inp_mouse_doubleclick() +{ + return release_delta < (time_freq() >> 2); +} + +int inp_key_presses(int key) +{ + return input_count[input_current][key].presses; +} + +int inp_key_releases(int key) +{ + return input_count[input_current][key].releases; +} + +int inp_key_state(int key) +{ + return input_state[input_current][key]; +} + +int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } +int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } +int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } +int inp_button_pressed(int button) { return keyboard_state[keyboard_current][button]; } + +void inp_update() +{ + int i, v; + + /* clear and begin count on the other one */ + mem_zero(&input_count[input_current], sizeof(input_count[input_current])); + memcpy(input_state[input_current], input_state[input_current^1], sizeof(input_state[input_current])); + input_current^=1; + + if(keyboard_first) + { + /* make sure to reset */ + keyboard_first = 0; + inp_update(); + } + + keyboard_current = keyboard_current^1; + for(i = 0; i < KEY_LAST; i++) + { + if (i >= KEY_MOUSE_FIRST) + v = glfwGetMouseButton(i-KEY_MOUSE_FIRST) == GLFW_PRESS ? 1 : 0; + else + v = glfwGetKey(i) == GLFW_PRESS ? 1 : 0; + keyboard_state[keyboard_current][i] = v; + } + + /* handle mouse wheel */ + /* + i = glfwGetMouseWheel(); + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 0; + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 0; + if(w > 0) + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 1; + if(w < 0) + keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 1; + glfwSetMouseWheel(0);*/ +} diff --git a/src/engine/client/ec_snd.c b/src/engine/client/ec_snd.c new file mode 100644 index 00000000..89e18cb8 --- /dev/null +++ b/src/engine/client/ec_snd.c @@ -0,0 +1,441 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include + +#include +#include +#include +#include +#include + +enum +{ + NUM_SAMPLES = 512, + NUM_VOICES = 64, + NUM_CHANNELS = 16, + + MAX_FRAMES = 1024 +}; + +typedef struct +{ + short *data; + int num_frames; + int rate; + int channels; + int loop_start; + int loop_end; +} SAMPLE; + +typedef struct +{ + int vol; + int pan; +} CHANNEL; + +typedef struct VOICE_t +{ + SAMPLE *snd; + CHANNEL *channel; + int tick; + int vol; /* 0 - 255 */ + int flags; + int x, y; +} VOICE; + +static SAMPLE samples[NUM_SAMPLES] = { {0} }; +static VOICE voices[NUM_VOICES] = { {0} }; +static CHANNEL channels[NUM_CHANNELS] = { {255, 0} }; + +static LOCK sound_lock = 0; +static int sound_enabled = 0; + +static int center_x = 0; +static int center_y = 0; + +static int mixing_rate = 48000; + +void snd_set_channel(int cid, float vol, float pan) +{ + channels[cid].vol = (int)(vol*255.0f); + channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */ +} + +static int play(int cid, int sid, int flags, float x, float y) +{ + int vid = -1; + int i; + + lock_wait(sound_lock); + + /* search for voice */ + /* TODO: fix this linear search */ + for(i = 0; i < NUM_VOICES; i++) + { + if(!voices[i].snd) + { + vid = i; + break; + } + } + + /* voice found, use it */ + if(vid != -1) + { + voices[vid].snd = &samples[sid]; + voices[vid].channel = &channels[cid]; + voices[vid].tick = 0; + voices[vid].vol = 255; + voices[vid].flags = flags; + voices[vid].x = (int)x; + voices[vid].y = (int)y; + } + + lock_release(sound_lock); + return vid; +} + +int snd_play_at(int cid, int sid, int flags, float x, float y) +{ + return play(cid, sid, flags|SNDFLAG_POS, x, y); +} + +int snd_play(int cid, int sid, int flags) +{ + return play(cid, sid, flags, 0, 0); +} + +void snd_stop(int vid) +{ + /* TODO: a nice fade out */ + lock_wait(sound_lock); + voices[vid].snd = 0; + lock_release(sound_lock); +} + +/* TODO: there should be a faster way todo this */ +static short int2short(int i) +{ + if(i > 0x7fff) + return 0x7fff; + else if(i < -0x7fff) + return -0x7fff; + return i; +} + +static int iabs(int i) +{ + if(i<0) + return -i; + return i; +} + +static void mix(short *final_out, unsigned frames) +{ + int mix_buffer[MAX_FRAMES*2] = {0}; + int i, s; + + /* aquire lock while we are mixing */ + lock_wait(sound_lock); + + for(i = 0; i < NUM_VOICES; i++) + { + if(voices[i].snd) + { + /* mix voice */ + VOICE *v = &voices[i]; + int *out = mix_buffer; + + int step = v->snd->channels; /* setup input sources */ + short *in_l = &v->snd->data[v->tick*step]; + short *in_r = &v->snd->data[v->tick*step+1]; + + int end = v->snd->num_frames-v->tick; + + int rvol = v->channel->vol; + int lvol = v->channel->vol; + + /* make sure that we don't go outside the sound data */ + if(frames < end) + end = frames; + + /* check if we have a mono sound */ + if(v->snd->channels == 1) + in_r = in_l; + + /* volume calculation */ + if(v->flags&SNDFLAG_POS && v->channel->pan) + { + /* TODO: we should respect the channel panning value */ + const int range = 1500; /* magic value, remove */ + int dx = v->x - center_x; + int dy = v->y - center_y; + int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */ + int p = iabs(dx); + if(dist < range) + { + /* panning */ + if(dx > 0) + lvol = ((range-p)*lvol)/range; + else + rvol = ((range-p)*rvol)/range; + + /* falloff */ + lvol = (lvol*(range-dist))/range; + rvol = (rvol*(range-dist))/range; + } + else + { + lvol = 0; + rvol = 0; + } + } + + /* process all frames */ + for(s = 0; s < end; s++) + { + *out++ += (*in_l)*lvol; + *out++ += (*in_r)*rvol; + in_l += step; + in_r += step; + v->tick++; + } + + /* free voice if not used any more */ + if(v->tick == v->snd->num_frames) + v->snd = 0; + + } + } + + /* release the lock */ + lock_release(sound_lock); + + { + int master_vol = config.snd_volume; + + /* clamp accumulated values */ + /* TODO: this seams slow */ + for(i = 0; i < frames; i++) + { + int j = i<<1; + int vl = ((mix_buffer[j]*master_vol)/101)>>8; + int vr = ((mix_buffer[j+1]*master_vol)/101)>>8; + + final_out[j] = int2short(vl); + final_out[j+1] = int2short(vr); + } + } +} + +static int pacallback(const void *in, void *out, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags status, void *user) +{ + mix(out, frames); + return 0; +} + +static PaStream *stream; + +int snd_init() +{ + PaStreamParameters params; + PaError err = Pa_Initialize(); + + sound_lock = lock_create(); + + if(!config.snd_enable) + return 0; + + mixing_rate = config.snd_rate; + + params.device = Pa_GetDefaultOutputDevice(); + if(params.device < 0) + return 1; + params.channelCount = 2; + params.sampleFormat = paInt16; + params.suggestedLatency = Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency; + params.hostApiSpecificStreamInfo = 0x0; + + err = Pa_OpenStream( + &stream, /* passes back stream pointer */ + 0, /* no input channels */ + ¶ms, /* pointer to parameters */ + mixing_rate, /* sample rate */ + 128, /* frames per buffer */ + paClipOff, /* no clamping */ + pacallback, /* specify our custom callback */ + 0x0); /* pass our data through to callback */ + err = Pa_StartStream(stream); + + sound_enabled = 1; + return 0; +} + +int snd_shutdown() +{ + Pa_StopStream(stream); + Pa_Terminate(); + + lock_destroy(sound_lock); + + return 0; +} + +int snd_alloc_id() +{ + /* TODO: linear search, get rid of it */ + unsigned sid; + for(sid = 0; sid < NUM_SAMPLES; sid++) + { + if(samples[sid].data == 0x0) + return sid; + } + + return -1; +} + +static void rate_convert(int sid) +{ + SAMPLE *snd = &samples[sid]; + int num_frames = 0; + short *new_data = 0; + int i; + + /* make sure that we need to convert this sound */ + if(!snd->data || snd->rate == mixing_rate) + return; + + /* allocate new data */ + num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate); + new_data = mem_alloc(num_frames*snd->channels*sizeof(short), 1); + + for(i = 0; i < num_frames; i++) + { + /* resample TODO: this should be done better, like linear atleast */ + float a = i/(float)num_frames; + int f = (int)(a*snd->num_frames); + if(f >= snd->num_frames) + f = snd->num_frames-1; + + /* set new data */ + if(snd->channels == 1) + new_data[i] = snd->data[f]; + else if(snd->channels == 2) + { + new_data[i*2] = snd->data[f*2]; + new_data[i*2+1] = snd->data[f*2+1]; + } + } + + /* free old data and apply new */ + mem_free(snd->data); + snd->data = new_data; + snd->num_frames = num_frames; +} + + +static FILE *file = NULL; + +static int read_data(void *buffer, int size) +{ + return fread(buffer, 1, size, file); +} + +int snd_load_wv(const char *filename) +{ + SAMPLE *snd; + int sid = -1; + char error[100]; + WavpackContext *context; + + /* don't waste memory on sound when we are stress testing */ + if(config.dbg_stress) + return -1; + + /* no need to load sound when we are running with no sound */ + if(!sound_enabled) + return 1; + + file = fopen(filename, "rb"); /* TODO: use system.h stuff for this */ + if(!file) + { + dbg_msg("sound/wv", "failed to open %s", filename); + return -1; + } + + sid = snd_alloc_id(); + if(sid < 0) + return -1; + snd = &samples[sid]; + + context = WavpackOpenFileInput(read_data, error); + if (context) + { + int samples = WavpackGetNumSamples(context); + int bitspersample = WavpackGetBitsPerSample(context); + unsigned int samplerate = WavpackGetSampleRate(context); + int channels = WavpackGetNumChannels(context); + int *data; + int *src; + short *dst; + int i; + + snd->channels = channels; + snd->rate = samplerate; + + if(snd->channels > 2) + { + dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); + return -1; + } + + /* + if(snd->rate != 44100) + { + dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); + return -1; + }*/ + + if(bitspersample != 16) + { + dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); + return -1; + } + + data = (int *)mem_alloc(4*samples*channels, 1); + WavpackUnpackSamples(context, data, samples); /* TODO: check return value */ + src = data; + + snd->data = (short *)mem_alloc(2*samples*channels, 1); + dst = snd->data; + + for (i = 0; i < samples*channels; i++) + *dst++ = (short)*src++; + + mem_free(data); + + snd->num_frames = samples; + snd->loop_start = -1; + snd->loop_end = -1; + } + else + { + dbg_msg("sound/wv", "failed to open %s: %s", filename, error); + } + + fclose(file); + file = NULL; + + if(config.debug) + dbg_msg("sound/wv", "loaded %s", filename); + + rate_convert(sid); + return sid; +} + +void snd_set_listener_pos(float x, float y) +{ + center_x = (int)x; + center_y = (int)y; +} diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.c new file mode 100644 index 00000000..9fc5ec02 --- /dev/null +++ b/src/engine/client/ec_srvbrowse.c @@ -0,0 +1,404 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include + +#include + +#include +#include + +extern NETCLIENT *net; + + +/* ------ server browse ---- */ +/* TODO: move all this to a separate file */ + +typedef struct SERVERENTRY_t SERVERENTRY; +struct SERVERENTRY_t +{ + NETADDR4 addr; + int64 request_time; + int got_info; + SERVER_INFO info; + + SERVERENTRY *next_ip; /* ip hashed list */ + + SERVERENTRY *prev_req; /* request list */ + SERVERENTRY *next_req; +}; + +static HEAP *serverlist_heap = 0; +static SERVERENTRY **serverlist = 0; +static int *sorted_serverlist = 0; + +static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ + +static SERVERENTRY *first_req_server = 0; /* request list */ +static SERVERENTRY *last_req_server = 0; +static int num_requests = 0; + +static int num_sorted_servers = 0; +static int num_sorted_servers_capacity = 0; +static int num_servers = 0; +static int num_server_capacity = 0; + +static int sorthash = 0; +static char filterstring[64] = {0}; + +static int serverlist_lan = 1; + +int client_serverbrowse_num() { return num_servers; } + +SERVER_INFO *client_serverbrowse_get(int index) +{ + if(index < 0 || index >= num_servers) + return 0; + return &serverlist[index]->info; +} + +int client_serverbrowse_sorted_num() { return num_sorted_servers; } + +SERVER_INFO *client_serverbrowse_sorted_get(int index) +{ + if(index < 0 || index >= num_sorted_servers) + return 0; + return &serverlist[sorted_serverlist[index]]->info; +} + + +int client_serverbrowse_num_requests() +{ + return num_requests; +} + +static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.name, b->info.name); +} + +static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return strcmp(a->info.map, b->info.map); +} + +static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.latency > b->info.latency; +} + +static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.game_type > b->info.game_type; +} + +static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.progression > b->info.progression; +} + +static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) +{ + SERVERENTRY *a = serverlist[*(const int*)ai]; + SERVERENTRY *b = serverlist[*(const int*)bi]; + return a->info.num_players > b->info.num_players; +} + +static void client_serverbrowse_filter() +{ + int i = 0; + num_sorted_servers = 0; + + /* allocate the sorted list */ + if(num_sorted_servers_capacity < num_servers) + { + if(sorted_serverlist) + mem_free(sorted_serverlist); + num_sorted_servers_capacity = num_servers; + sorted_serverlist = mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); + } + + /* filter the servers */ + for(i = 0; i < num_servers; i++) + { + int filtered = 0; + + if(config.b_filter_empty && serverlist[i]->info.num_players == 0) + filtered = 1; + else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) + filtered = 1; + else if(config.b_filter_pw && serverlist[i]->info.flags&1) + filtered = 1; + else if(config.b_filter_string[0] != 0) + { + if(strstr(serverlist[i]->info.name, config.b_filter_string) == 0) + filtered = 1; + } + + if(filtered == 0) + sorted_serverlist[num_sorted_servers++] = i; + } +} + +static int client_serverbrowse_sorthash() +{ + int i = config.b_sort&0xf; + i |= config.b_filter_empty<<4; + i |= config.b_filter_full<<5; + i |= config.b_filter_pw<<6; + return i; +} + +static void client_serverbrowse_sort() +{ + int i; + + /* create filtered list */ + client_serverbrowse_filter(); + + /* sort */ + if(config.b_sort == BROWSESORT_NAME) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); + else if(config.b_sort == BROWSESORT_PING) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); + else if(config.b_sort == BROWSESORT_MAP) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); + else if(config.b_sort == BROWSESORT_NUMPLAYERS) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); + else if(config.b_sort == BROWSESORT_GAMETYPE) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); + else if(config.b_sort == BROWSESORT_PROGRESSION) + qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); + + /* set indexes */ + for(i = 0; i < num_sorted_servers; i++) + serverlist[sorted_serverlist[i]]->info.sorted_index = i; + + strncpy(filterstring, config.b_filter_string, sizeof(filterstring)-1); + sorthash = client_serverbrowse_sorthash(); +} + +static void client_serverbrowse_remove_request(SERVERENTRY *entry) +{ + if(entry->prev_req || entry->next_req || first_req_server == entry) + { + if(entry->prev_req) + entry->prev_req->next_req = entry->next_req; + else + first_req_server = entry->next_req; + + if(entry->next_req) + entry->next_req->prev_req = entry->prev_req; + else + last_req_server = entry->prev_req; + + entry->prev_req = 0; + entry->next_req = 0; + num_requests--; + } +} + +void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info) +{ + int hash = addr->ip[0]; + SERVERENTRY *entry = serverlist_ip[hash]; + while(entry) + { + if(net_addr4_cmp(&entry->addr, addr) == 0) + { + /* update the server that we already have */ + entry->info = *info; + if(!request) + { + entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); + client_serverbrowse_remove_request(entry); + } + + entry->got_info = 1; + client_serverbrowse_sort(); + return; + } + entry = entry->next_ip; + } + + /* create new entry */ + entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); + mem_zero(entry, sizeof(SERVERENTRY)); + + /* set the info */ + entry->addr = *addr; + entry->info = *info; + + /* add to the hash list */ + entry->next_ip = serverlist_ip[hash]; + serverlist_ip[hash] = entry; + + if(num_servers == num_server_capacity) + { + SERVERENTRY **newlist; + num_server_capacity += 100; + newlist = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); + memcpy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); + mem_free(serverlist); + serverlist = newlist; + } + + /* add to list */ + serverlist[num_servers] = entry; + entry->info.server_index = num_servers; + num_servers++; + + /* */ + if(request) + { + /* add it to the list of servers that we should request info from */ + entry->prev_req = last_req_server; + if(last_req_server) + last_req_server->next_req = entry; + else + first_req_server = entry; + last_req_server = entry; + + num_requests++; + } + + client_serverbrowse_sort(); +} + +void client_serverbrowse_refresh(int lan) +{ + /* clear out everything */ + if(serverlist_heap) + memheap_destroy(serverlist_heap); + serverlist_heap = memheap_create(); + num_servers = 0; + num_sorted_servers = 0; + mem_zero(serverlist_ip, sizeof(serverlist_ip)); + first_req_server = 0; + last_req_server = 0; + num_requests = 0; + + + /* */ + serverlist_lan = lan; + + if(serverlist_lan) + { + NETPACKET packet; + packet.client_id = -1; + mem_zero(&packet, sizeof(packet)); + packet.address.ip[0] = 255; + packet.address.ip[1] = 255; + packet.address.ip[2] = 255; + packet.address.ip[3] = 255; + packet.address.port = 8303; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_GETINFO); + packet.data = SERVERBROWSE_GETINFO; + netclient_send(net, &packet); + + if(config.debug) + dbg_msg("client", "broadcasting for servers"); + } + else + { + NETADDR4 master_server; + NETPACKET p; + + net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server); + + mem_zero(&p, sizeof(p)); + p.client_id = -1; + p.address = master_server; + p.flags = PACKETFLAG_CONNLESS; + p.data_size = sizeof(SERVERBROWSE_GETLIST); + p.data = SERVERBROWSE_GETLIST; + netclient_send(net, &p); + + if(config.debug) + dbg_msg("client", "requesting server list"); + } +} + +static void client_serverbrowse_request(SERVERENTRY *entry) +{ + NETPACKET p; + + if(config.debug) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + entry->addr.ip[0], entry->addr.ip[1], entry->addr.ip[2], + entry->addr.ip[3], entry->addr.port); + } + + p.client_id = -1; + p.address = entry->addr; + p.flags = PACKETFLAG_CONNLESS; + p.data_size = sizeof(SERVERBROWSE_GETINFO); + p.data = SERVERBROWSE_GETINFO; + netclient_send(net, &p); + entry->request_time = time_get(); +} + +void client_serverbrowse_update() +{ + int64 timeout = time_freq(); + int64 now = time_get(); + int count; + SERVERENTRY *entry, *next; + + /* do timeouts */ + entry = first_req_server; + while(1) + { + if(!entry) /* no more entries */ + break; + + next = entry->next_req; + + if(entry->request_time && entry->request_time+timeout < now) + { + /* timeout */ + client_serverbrowse_remove_request(entry); + num_requests--; + } + + entry = next; + } + + /* do timeouts */ + entry = first_req_server; + count = 0; + while(1) + { + if(!entry) /* no more entries */ + break; + + /* no more then 10 concurrent requests */ + if(count == config.b_max_requests) + break; + + if(entry->request_time == 0) + client_serverbrowse_request(entry); + + count++; + entry = entry->next_req; + } + + /* check if we need to resort */ + /* TODO: remove the strcmp */ + if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0) + client_serverbrowse_sort(); +} diff --git a/src/engine/client/ec_ui.c b/src/engine/client/ec_ui.c new file mode 100644 index 00000000..5ba9c0d9 --- /dev/null +++ b/src/engine/client/ec_ui.c @@ -0,0 +1,277 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include "ec_ui.h" + +/******************************************************** + UI +*********************************************************/ + +struct pretty_font +{ + float m_CharStartTable[256]; + float m_CharEndTable[256]; + int font_texture; +}; + +extern struct pretty_font *current_font; + +static const void *hot_item = 0; +static const void *active_item = 0; +static const void *last_active_item = 0; +static const void *becomming_hot_item = 0; +static float mouse_x, mouse_y; /* in gui space */ +static float mouse_wx, mouse_wy; /* in world space */ +static unsigned mouse_buttons = 0; + +float ui_mouse_x() { return mouse_x; } +float ui_mouse_y() { return mouse_y; } +float ui_mouse_world_x() { return mouse_wx; } +float ui_mouse_world_y() { return mouse_wy; } +int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } + +void ui_set_hot_item(const void *id) { becomming_hot_item = id; } +void ui_set_active_item(const void *id) { active_item = id; if (id) last_active_item = id; } +void ui_clear_last_active_item() { last_active_item = 0; } +const void *ui_hot_item() { return hot_item; } +const void *ui_active_item() { return active_item; } +const void *ui_last_active_item() { return last_active_item; } + +int ui_update(float mx, float my, float mwx, float mwy, int buttons) +{ + mouse_x = mx; + mouse_y = my; + mouse_wx = mwx; + mouse_wy = mwy; + mouse_buttons = buttons; + hot_item = becomming_hot_item; + if(active_item) + hot_item = active_item; + becomming_hot_item = 0; + return 0; +} + +int ui_mouse_inside(float x, float y, float w, float h) +{ + if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) + return 1; + return 0; +} + +void ui_do_image(int texture, float x, float y, float w, float h) +{ + gfx_blend_normal(); + gfx_texture_set(texture); + gfx_quads_begin(); + gfx_setcolor(1,1,1,1); + gfx_quads_setsubset( + 0.0f, /* startx */ + 0.0f, /* starty */ + 1.0f, /* endx */ + 1.0f); /* endy */ + gfx_quads_drawTL(x,y,w,h); + gfx_quads_end(); +} + +void ui_do_label(float x, float y, const char *text, float size) +{ + gfx_blend_normal(); + gfx_texture_set(current_font->font_texture); + gfx_pretty_text(x, y, size, text, -1); +} + +int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) +{ + /* logic */ + int r = 0; + int inside = ui_mouse_inside(x,y,w,h); + + if(ui_active_item() == id) + { + if(!ui_mouse_button(0)) + { + if(inside) + r = 1; + ui_set_active_item(0); + } + } + else if(ui_hot_item() == id) + { + if(ui_mouse_button(0)) + ui_set_active_item(id); + } + + if(inside) + ui_set_hot_item(id); + + draw_func(id, text, checked, x, y, w, h, extra); + return r; +} + +static float scale = 1.0f; +#define MEMORY_SIZE 10*1024 +static struct rect memory[MEMORY_SIZE]; +static int memory_used = 0; +static struct rect screen = { 0.0f, 0.0f, 848.0f, 480.0f }; + +void ui_foreach_rect(rect_fun fun) +{ + int hrm; + for (hrm = 0; hrm < memory_used; hrm++) + fun(&memory[hrm]); +} + +static void add_rect(struct rect *r) +{ + if (memory_used < MEMORY_SIZE) + memory[memory_used++] = *r; + else + dbg_msg("ui", "rect memory full."); +} + +struct rect *ui_screen() +{ + if (config.debug) + { + memory_used = 0; + } + + return &screen; +} + +void ui_scale(float s) +{ + scale = s; +} + +void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) +{ + struct rect r = *original; + pixels *= scale; + + if (top) + { + top->x = r.x; + top->y = r.y; + top->w = r.w; + top->h = pixels; + } + + if (bottom) + { + bottom->x = r.x; + bottom->y = r.y + pixels; + bottom->w = r.w; + bottom->h = r.h - pixels; + } + + if (config.debug) + { + if (top) + add_rect(top); + if (bottom) + add_rect(bottom); + } +} + +void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) +{ + struct rect r = *original; + pixels *= scale; + + if (top) + { + top->x = r.x; + top->y = r.y; + top->w = r.w; + top->h = r.h - pixels; + } + + if (bottom) + { + bottom->x = r.x; + bottom->y = r.y + r.h - pixels; + bottom->w = r.w; + bottom->h = pixels; + } + + if (config.debug) + { + if (top) + add_rect(top); + if (bottom) + add_rect(bottom); + } +} + +void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right) +{ + struct rect r = *original; + pixels *= scale; + + if (left) + { + left->x = r.x; + left->y = r.y; + left->w = pixels; + left->h = r.h; + } + + if (right) + { + right->x = r.x + pixels; + right->y = r.y; + right->w = r.w - pixels; + right->h = r.h; + } + + if (config.debug) + { + if (left) + add_rect(left); + if (right) + add_rect(right); + } +} + +void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right) +{ + struct rect r = *original; + pixels *= scale; + + if (left) + { + left->x = r.x; + left->y = r.y; + left->w = r.w - pixels; + left->h = r.h; + } + + if (right) + { + right->x = r.x + r.w - pixels; + right->y = r.y; + right->w = pixels; + right->h = r.h; + } + + if (config.debug) + { + if (left) + add_rect(left); + if (right) + add_rect(right); + } +} + +void ui_margin(const struct rect *original, int pixels, struct rect *other_rect) +{ + struct rect r = *original; + pixels *= scale; + + other_rect->x = r.x + pixels; + other_rect->y = r.y + pixels; + other_rect->w = r.w - 2*pixels; + other_rect->h = r.h - 2*pixels; +} diff --git a/src/engine/client/ec_ui.h b/src/engine/client/ec_ui.h new file mode 100644 index 00000000..15c63b90 --- /dev/null +++ b/src/engine/client/ec_ui.h @@ -0,0 +1,36 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef _UI_H +#define _UI_H + +#ifdef __cplusplus +extern "C" { +#endif + +int ui_update(float mx, float my, float mwx, float mwy, int buttons); + +float ui_mouse_x(); +float ui_mouse_y(); +float ui_mouse_world_x(); +float ui_mouse_world_y(); +int ui_mouse_button(int index); + +void ui_set_hot_item(const void *id); +void ui_set_active_item(const void *id); +void ui_clear_last_active_item(); +const void *ui_hot_item(); +const void *ui_active_item(); +const void *ui_last_active_item(); + +int ui_mouse_inside(float x, float y, float w, float h); + +typedef void (*draw_button_callback)(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); + +void ui_do_image(int texture, float x, float y, float w, float h); +void ui_do_label(float x, float y, const char *text, float size); +int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/client/gfx.c b/src/engine/client/gfx.c deleted file mode 100644 index c61f0cb1..00000000 --- a/src/engine/client/gfx.c +++ /dev/null @@ -1,1018 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include - -/* compressed textures */ -#define GL_COMPRESSED_RGB_ARB 0x84ED -#define GL_COMPRESSED_RGBA_ARB 0x84EE - -enum -{ - DRAWING_QUADS=1, - DRAWING_LINES=2 -}; - -/* */ -typedef struct { float x, y, z; } VEC3; -typedef struct { float u, v; } TEXCOORD; -typedef struct { float r, g, b, a; } COLOR; - -typedef struct -{ - VEC3 pos; - TEXCOORD tex; - COLOR color; -} VERTEX; - -const int vertex_buffer_size = 32*1024; -static VERTEX *vertices = 0; -static int num_vertices = 0; - -static COLOR color[4]; -static TEXCOORD texture[4]; - -static int do_screenshot = 0; - -static int screen_width = -1; -static int screen_height = -1; -static float rotation = 0; -static int drawing = 0; - -static float screen_x0 = 0; -static float screen_y0 = 0; -static float screen_x1 = 0; -static float screen_y1 = 0; - -typedef struct -{ - GLuint tex; - int memsize; - int flags; - int next; -} TEXTURE; - - -enum -{ - MAX_TEXTURES = 128 -}; - -static TEXTURE textures[MAX_TEXTURES]; -static int first_free_texture; -static int memory_usage = 0; - -static const unsigned char null_texture_data[] = { - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0x00,0xff,0x00,0xff, 0x00,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, - 0x00,0x00,0xff,0xff, 0x00,0x00,0xff,0xff, 0xff,0xff,0x00,0xff, 0xff,0xff,0x00,0xff, -}; - - -typedef struct -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -} pretty_font; - -pretty_font default_font = -{ -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0.421875, 0.359375, 0.265625, 0.25, 0.1875, 0.25, 0.4375, 0.390625, 0.390625, 0.34375, 0.28125, 0.421875, 0.390625, 0.4375, 0.203125, - 0.265625, 0.28125, 0.28125, 0.265625, 0.25, 0.28125, 0.28125, 0.265625, 0.28125, 0.265625, 0.4375, 0.421875, 0.3125, 0.28125, 0.3125, 0.3125, - 0.25, 0.234375, 0.28125, 0.265625, 0.265625, 0.296875, 0.3125, 0.25, 0.25, 0.421875, 0.28125, 0.265625, 0.328125, 0.171875, 0.234375, 0.25, - 0.28125, 0.234375, 0.265625, 0.265625, 0.28125, 0.265625, 0.234375, 0.09375, 0.234375, 0.234375, 0.265625, 0.390625, 0.203125, 0.390625, 0.296875, 0.28125, - 0.375, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.359375, 0.296875, 0.3125, 0.4375, 0.390625, 0.328125, 0.4375, 0.203125, 0.3125, 0.296875, - 0.3125, 0.296875, 0.359375, 0.3125, 0.328125, 0.3125, 0.296875, 0.203125, 0.296875, 0.296875, 0.328125, 0.375, 0.421875, 0.375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, - 0, 0.421875, 0.3125, 0.265625, 0.25, 0.25, 0.421875, 0.265625, 0.375, 0.21875, 0.375, 0.328125, 0.3125, 0, 0.21875, 0.28125, - 0.359375, 0.28125, 0.34375, 0.34375, 0.421875, 0.3125, 0.265625, 0.421875, 0.421875, 0.34375, 0.375, 0.328125, 0.125, 0.125, 0.125, 0.296875, - 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.234375, 0.109375, 0.265625, 0.296875, 0.296875, 0.296875, 0.296875, 0.375, 0.421875, 0.359375, 0.390625, - 0.21875, 0.234375, 0.25, 0.25, 0.25, 0.25, 0.25, 0.296875, 0.21875, 0.265625, 0.265625, 0.265625, 0.265625, 0.234375, 0.28125, 0.3125, - 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.1875, 0.3125, 0.3125, 0.3125, 0.3125, 0.3125, 0.375, 0.421875, 0.359375, 0.390625, - 0.3125, 0.3125, 0.296875, 0.296875, 0.296875, 0.296875, 0.296875, 0.28125, 0.28125, 0.3125, 0.3125, 0.3125, 0.3125, 0.296875, 0.3125, 0.296875, -}, -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0.2, 0.5625, 0.625, 0.71875, 0.734375, 0.796875, 0.765625, 0.546875, 0.59375, 0.59375, 0.65625, 0.703125, 0.546875, 0.59375, 0.5625, 0.6875, - 0.71875, 0.609375, 0.703125, 0.703125, 0.71875, 0.703125, 0.703125, 0.6875, 0.703125, 0.703125, 0.5625, 0.546875, 0.671875, 0.703125, 0.671875, 0.671875, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.6875, 0.6875, 0.734375, 0.71875, 0.5625, 0.65625, 0.765625, 0.703125, 0.8125, 0.75, 0.734375, - 0.734375, 0.765625, 0.71875, 0.71875, 0.703125, 0.71875, 0.75, 0.890625, 0.75, 0.75, 0.71875, 0.59375, 0.6875, 0.59375, 0.6875, 0.703125, - 0.5625, 0.671875, 0.6875, 0.671875, 0.671875, 0.671875, 0.625, 0.671875, 0.671875, 0.5625, 0.546875, 0.703125, 0.5625, 0.78125, 0.671875, 0.671875, - 0.6875, 0.671875, 0.65625, 0.671875, 0.65625, 0.671875, 0.6875, 0.78125, 0.6875, 0.671875, 0.65625, 0.609375, 0.546875, 0.609375, 0.703125, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, - 0, 0.5625, 0.671875, 0.734375, 0.734375, 0.734375, 0.546875, 0.71875, 0.609375, 0.765625, 0.609375, 0.65625, 0.671875, 0, 0.765625, 0.703125, - 0.625, 0.703125, 0.640625, 0.640625, 0.609375, 0.671875, 0.703125, 0.546875, 0.5625, 0.578125, 0.609375, 0.65625, 0.859375, 0.859375, 0.859375, 0.671875, - 0.75, 0.75, 0.75, 0.75, 0.75, 0.75, 0.84375, 0.734375, 0.6875, 0.6875, 0.6875, 0.6875, 0.5625, 0.609375, 0.640625, 0.59375, - 0.734375, 0.75, 0.734375, 0.734375, 0.734375, 0.734375, 0.734375, 0.6875, 0.765625, 0.71875, 0.71875, 0.71875, 0.71875, 0.75, 0.734375, 0.6875, - 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.796875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.5625, 0.609375, 0.625, 0.59375, - 0.6875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.703125, 0.703125, 0.671875, 0.671875, 0.671875, 0.671875, 0.671875, 0.6875, 0.671875, -}, -0 -}; - -double extra_kerning[256*256] = {0}; - -pretty_font *current_font = &default_font; - -static void flush() -{ - if(num_vertices == 0) - return; - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - glVertexPointer(3, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices); - glTexCoordPointer(2, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices + sizeof(float)*3); - glColorPointer(4, GL_FLOAT, - sizeof(VERTEX), - (char*)vertices + sizeof(float)*5); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - if(drawing == DRAWING_QUADS) - glDrawArrays(GL_QUADS, 0, num_vertices); - else if(drawing == DRAWING_LINES) - glDrawArrays(GL_LINES, 0, num_vertices); - - /* Reset pointer */ - num_vertices = 0; -} - -static void draw_line() -{ - num_vertices += 2; - if((num_vertices + 2) >= vertex_buffer_size) - flush(); -} - -static void draw_quad() -{ - num_vertices += 4; - if((num_vertices + 4) >= vertex_buffer_size) - flush(); -} - - -int gfx_init() -{ - int i; - - screen_width = config.gfx_screen_width; - screen_height = config.gfx_screen_height; - - glfwInit(); - - if(config.dbg_stress) - { - screen_width = 320; - screen_height = 240; - } - - /* set antialiasing */ - if(config.gfx_fsaa_samples) - glfwOpenWindowHint(GLFW_FSAA_SAMPLES, config.gfx_fsaa_samples); - - /* set refresh rate */ - if(config.gfx_refresh_rate) - glfwOpenWindowHint(GLFW_REFRESH_RATE, config.gfx_refresh_rate); - - /* no resizing allowed */ - glfwOpenWindowHint(GLFW_WINDOW_NO_RESIZE, 1); - - /* open window */ - if(config.gfx_fullscreen) - { - int result = glfwOpenWindow(screen_width, screen_height, 8, 8, 8, 0, 24, 0, GLFW_FULLSCREEN); - if(result != GL_TRUE) - { - dbg_msg("game", "failed to create gl context"); - return 0; - } - } - else - { - int result = glfwOpenWindow(screen_width, screen_height, 0, 0, 0, 0, 24, 0, GLFW_WINDOW); - if(result != GL_TRUE) - { - dbg_msg("game", "failed to create gl context"); - return 0; - } - } - - glGetIntegerv(GL_DEPTH_BITS, &i); - dbg_msg("gfx", "depthbits = %d", i); - - glfwSetWindowTitle("Teewars"); - - /* We don't want to see the window when we run the stress testing */ - if(config.dbg_stress) - glfwIconifyWindow(); - - /* Init vertices */ - if (vertices) - mem_free(vertices); - vertices = (VERTEX*)mem_alloc(sizeof(VERTEX) * vertex_buffer_size, 1); - num_vertices = 0; - - - /* - dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(), - context.version_minor(), - context.version_rev());*/ - - gfx_mapscreen(0,0,config.gfx_screen_width, config.gfx_screen_height); - - /* set some default settings */ - glEnable(GL_BLEND); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gfx_mask_op(MASK_NONE, 0); - - /* Set all z to -5.0f */ - for (i = 0; i < vertex_buffer_size; i++) - vertices[i].pos.z = -5.0f; - - /* init textures */ - first_free_texture = 0; - for(i = 0; i < MAX_TEXTURES; i++) - textures[i].next = i+1; - textures[MAX_TEXTURES-1].next = -1; - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - /* init input */ - inp_init(); - - /* create null texture, will get id=0 */ - gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data); - - /* set vsync as needed */ - gfx_set_vsync(config.gfx_vsync); - - /* UGLY as hell, please remove */ - current_font->font_texture = gfx_load_texture("data/big_font.png"); - - return 1; -} - -float gfx_screenaspect() -{ - return gfx_screenwidth()/(float)gfx_screenheight(); -} - -void gfx_clear_mask(int fill) -{ - /*if(fill) - glClearDepth(0.0f); - else*/ - - int i; - glGetIntegerv(GL_DEPTH_WRITEMASK, &i); - glDepthMask(GL_TRUE); - glClearDepth(0.0f); - glClear(GL_DEPTH_BUFFER_BIT); - glDepthMask(i); -} - -void gfx_mask_op(int mask, int write) -{ - glEnable(GL_DEPTH_TEST); - - if(write) - glDepthMask(GL_TRUE); - else - glDepthMask(GL_FALSE); - - if(mask == MASK_NONE) - glDepthFunc(GL_ALWAYS); - else if(mask == MASK_SET) - glDepthFunc(GL_LEQUAL); - else if(mask == MASK_ZERO) - glDepthFunc(GL_NOTEQUAL); -} - -int gfx_window_active() -{ - return glfwGetWindowParam(GLFW_ACTIVE) == GL_TRUE ? 1 : 0; -} - -int gfx_window_open() -{ - return glfwGetWindowParam(GLFW_OPENED) == GL_TRUE ? 1 : 0; -} - -VIDEO_MODE fakemodes[] = { - {320,240,8,8,8}, {400,300,8,8,8}, {640,480,8,8,8}, - {720,400,8,8,8}, {768,576,8,8,8}, {800,600,8,8,8}, - {1024,600,8,8,8}, {1024,768,8,8,8}, {1152,864,8,8,8}, - {1280,768,8,8,8}, {1280,800,8,8,8}, {1280,960,8,8,8}, - {1280,1024,8,8,8}, {1368,768,8,8,8}, {1400,1050,8,8,8}, - {1440,900,8,8,8}, {1440,1050,8,8,8}, {1600,1000,8,8,8}, - {1600,1200,8,8,8}, {1680,1050,8,8,8}, {1792,1344,8,8,8}, - {1800,1440,8,8,8}, {1856,1392,8,8,8}, {1920,1080,8,8,8}, - {1920,1200,8,8,8}, {1920,1440,8,8,8}, {1920,2400,8,8,8}, - {2048,1536,8,8,8}, - - {320,240,5,6,5}, {400,300,5,6,5}, {640,480,5,6,5}, - {720,400,5,6,5}, {768,576,5,6,5}, {800,600,5,6,5}, - {1024,600,5,6,5}, {1024,768,5,6,5}, {1152,864,5,6,5}, - {1280,768,5,6,5}, {1280,800,5,6,5}, {1280,960,5,6,5}, - {1280,1024,5,6,5}, {1368,768,5,6,5}, {1400,1050,5,6,5}, - {1440,900,5,6,5}, {1440,1050,5,6,5}, {1600,1000,5,6,5}, - {1600,1200,5,6,5}, {1680,1050,5,6,5}, {1792,1344,5,6,5}, - {1800,1440,5,6,5}, {1856,1392,5,6,5}, {1920,1080,5,6,5}, - {1920,1200,5,6,5}, {1920,1440,5,6,5}, {1920,2400,5,6,5}, - {2048,1536,5,6,5} -}; - -int gfx_get_video_modes(VIDEO_MODE *list, int maxcount) -{ - if(config.gfx_display_all_modes) - { - int count = sizeof(fakemodes)/sizeof(VIDEO_MODE); - mem_copy(list, fakemodes, sizeof(fakemodes)); - if(maxcount < count) - count = maxcount; - return count; - } - - return glfwGetVideoModes((GLFWvidmode *)list, maxcount); -} - -void gfx_set_vsync(int val) -{ - glfwSwapInterval(val); -} - -int gfx_unload_texture(int index) -{ - glDeleteTextures(1, &textures[index].tex); - textures[index].next = first_free_texture; - memory_usage -= textures[index].memsize; - first_free_texture = index; - return 0; -} - -void gfx_blend_normal() -{ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void gfx_blend_additive() -{ - glBlendFunc(GL_SRC_ALPHA, GL_ONE); -} - -int gfx_memory_usage() { return memory_usage; } - -static unsigned char sample(int w, int h, const unsigned char *data, int u, int v, int offset) -{ - return (data[(v*w+u)*4+offset]+ - data[(v*w+u+1)*4+offset]+ - data[((v+1)*w+u)*4+offset]+ - data[((v+1)*w+u+1)*4+offset])/4; -} - -int gfx_load_texture_raw(int w, int h, int format, const void *data) -{ - int mipmap = 1; - unsigned char *texdata = (unsigned char *)data; - unsigned char *tmpdata = 0; - int oglformat = 0; - int tex = 0; - - /* don't waste memory on texture if we are stress testing */ - if(config.dbg_stress) - return -1; - - /* grab texture */ - tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - /* resample if needed */ - if(config.gfx_texture_quality==0) - { - if(w > 16 && h > 16 && format == IMG_RGBA) - { - unsigned char *tmpdata; - int c = 0; - int x, y; - - tmpdata = (unsigned char *)mem_alloc(w*h*4, 1); - - w/=2; - h/=2; - - for(y = 0; y < h; y++) - for(x = 0; x < w; x++, c++) - { - tmpdata[c*4] = sample(w*2, h*2, texdata, x*2,y*2, 0); - tmpdata[c*4+1] = sample(w*2, h*2, texdata, x*2,y*2, 1); - tmpdata[c*4+2] = sample(w*2, h*2, texdata, x*2,y*2, 2); - tmpdata[c*4+3] = sample(w*2, h*2, texdata, x*2,y*2, 3); - } - texdata = tmpdata; - } - } - - if(config.debug) - dbg_msg("gfx", "%d = %dx%d", tex, w, h); - - /* upload texture */ - if(config.gfx_texture_compression) - { - oglformat = GL_COMPRESSED_RGBA_ARB; - if(format == IMG_RGB) - oglformat = GL_COMPRESSED_RGB_ARB; - } - else - { - oglformat = GL_RGBA; - if(format == IMG_RGB) - oglformat = GL_RGB; - } - - glGenTextures(1, &textures[tex].tex); - glBindTexture(GL_TEXTURE_2D, textures[tex].tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - gluBuild2DMipmaps(GL_TEXTURE_2D, oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); - - /* calculate memory usage */ - textures[tex].memsize = w*h*4; - if(mipmap) - { - while(w > 2 && h > 2) - { - w>>=1; - h>>=1; - textures[tex].memsize += w*h*4; - } - } - - memory_usage += textures[tex].memsize; - mem_free(tmpdata); - return tex; -} -/* -int gfx_load_mip_texture_raw(int w, int h, int format, const void *data) -{ - // grab texture - int tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - // set data and return - // TODO: should be RGBA, not BGRA - dbg_msg("gfx", "%d = %dx%d", tex, w, h); - dbg_assert(format == IMG_RGBA, "not an RGBA image"); - - unsigned mip_w = w; - unsigned mip_h = h; - unsigned level = 0; - const unsigned char *ptr = (const unsigned char*)data; - while(mip_w > 0 && mip_h > 0) - { - dbg_msg("gfx mip", "%d = %dx%d", level, mip_w, mip_h); - textures[tex].tex.data2d_mip(mip_w, mip_h, GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE, level, ptr); - level++; - ptr = ptr + mip_w*mip_h*4; - mip_w = mip_w>>1; - mip_h = mip_h>>1; - } - return tex; -} -*/ - -/* simple uncompressed RGBA loaders */ -int gfx_load_texture(const char *filename) -{ - int l = strlen(filename); - IMAGE_INFO img; - if(l < 3) - return 0; - if(gfx_load_png(&img, filename)) - { - int id = gfx_load_texture_raw(img.width, img.height, img.format, img.data); - mem_free(img.data); - return id; - } - - return 0; -} - -int gfx_load_png(IMAGE_INFO *img, const char *filename) -{ - unsigned char *buffer; - png_t png; - - /* open file for reading */ - png_init(0,0); - - if(png_open_file(&png, filename) != PNG_NO_ERROR) - { - dbg_msg("game/png", "failed to open file. filename='%s'", filename); - return 0; - } - - if(png.depth != 8 || (png.color_type != PNG_TRUECOLOR && png.color_type != PNG_TRUECOLOR_ALPHA)) - { - dbg_msg("game/png", "invalid format. filename='%s'", filename); - png_close_file(&png); - } - - buffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); - png_get_data(&png, buffer); - png_close_file(&png); - - img->width = png.width; - img->height = png.height; - if(png.color_type == PNG_TRUECOLOR) - img->format = IMG_RGB; - else if(png.color_type == PNG_TRUECOLOR_ALPHA) - img->format = IMG_RGBA; - img->data = buffer; - return 1; -} - -void gfx_shutdown() -{ - if (vertices) - mem_free(vertices); - glfwCloseWindow(); - glfwTerminate(); -} - -void gfx_screenshot() -{ - do_screenshot = 1; -} - -void gfx_swap() -{ - if(do_screenshot) - { - /* fetch image data */ - int y; - int w = screen_width; - int h = screen_height; - unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*3, 1); - unsigned char *temp_row = pixel_data+w*h*3; - glReadPixels(0,0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixel_data); - - /* flip the pixel because opengl works from bottom left corner */ - for(y = 0; y < h/2; y++) - { - mem_copy(temp_row, pixel_data+y*w*3, w*3); - mem_copy(pixel_data+y*w*3, pixel_data+(h-y-1)*w*3, w*3); - mem_copy(pixel_data+(h-y-1)*w*3, temp_row,w*3); - } - - /* find filename */ - { - char wholepath[1024]; - char filename[128]; - static int index = 1; - png_t png; - - for(; index < 1000; index++) - { - IOHANDLE io; - sprintf(filename, "screenshots/screenshot%04d.png", index); - engine_savepath(filename, wholepath, sizeof(wholepath)); - - io = io_open(wholepath, IOFLAG_READ); - if(io) - io_close(io); - else - break; - } - - /* save png */ - dbg_msg("client", "saved screenshot to '%s'", wholepath); - png_open_file_write(&png, wholepath); - png_set_data(&png, w, h, 8, PNG_TRUECOLOR, (unsigned char *)pixel_data); - png_close_file(&png); - } - - /* clean up */ - mem_free(pixel_data); - do_screenshot = 0; - } - - glfwSwapBuffers(); - glFinish(); - glfwPollEvents(); -} - -int gfx_screenwidth() -{ - return screen_width; -} - -int gfx_screenheight() -{ - return screen_height; -} - -void gfx_texture_set(int slot) -{ - dbg_assert(drawing == 0, "called gfx_texture_set within begin"); - if(slot == -1) - glDisable(GL_TEXTURE_2D); - else - { - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, textures[slot].tex); - } -} - -void gfx_clear(float r, float g, float b) -{ - glClearColor(r,g,b,1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); -} - -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y) -{ - screen_x0 = tl_x; - screen_y0 = tl_y; - screen_x1 = br_x; - screen_y1 = br_y; - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); -} - -void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y) -{ - *tl_x = screen_x0; - *tl_y = screen_y0; - *br_x = screen_x1; - *br_y = screen_y1; -} - -void gfx_setoffset(float x, float y) -{ - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glTranslatef(x, y, 0); -} - - -void gfx_quads_begin() -{ - dbg_assert(drawing == 0, "called quads_begin twice"); - drawing = DRAWING_QUADS; - - gfx_quads_setsubset(0,0,1,1); - gfx_quads_setrotation(0); - gfx_setcolor(1,1,1,1); -} - -void gfx_quads_end() -{ - dbg_assert(drawing == DRAWING_QUADS, "called quads_end without begin"); - flush(); - drawing = 0; -} - - -void gfx_quads_setrotation(float angle) -{ - dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setrotation without begin"); - rotation = angle; -} - -void gfx_setcolorvertex(int i, float r, float g, float b, float a) -{ - dbg_assert(drawing != 0, "called gfx_quads_setcolorvertex without begin"); - color[i].r = r; - color[i].g = g; - color[i].b = b; - color[i].a = a; -} - -void gfx_setcolor(float r, float g, float b, float a) -{ - dbg_assert(drawing != 0, "called gfx_quads_setcolor without begin"); - gfx_setcolorvertex(0, r, g, b, a); - gfx_setcolorvertex(1, r, g, b, a); - gfx_setcolorvertex(2, r, g, b, a); - gfx_setcolorvertex(3, r, g, b, a); -} - -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v) -{ - dbg_assert(drawing == DRAWING_QUADS, "called gfx_quads_setsubset without begin"); - - texture[0].u = tl_u; - texture[0].v = tl_v; - - texture[1].u = br_u; - texture[1].v = tl_v; - - texture[2].u = br_u; - texture[2].v = br_v; - - texture[3].u = tl_u; - texture[3].v = br_v; -} - -static void rotate(VEC3 *center, VEC3 *point) -{ - float x = point->x - center->x; - float y = point->y - center->y; - point->x = x * cosf(rotation) - y * sinf(rotation) + center->x; - point->y = x * sinf(rotation) + y * cosf(rotation) + center->y; -} - -void gfx_quads_draw(float x, float y, float w, float h) -{ - gfx_quads_drawTL(x-w/2, y-h/2,w,h); -} - -void gfx_quads_drawTL(float x, float y, float width, float height) -{ - VEC3 center; - - dbg_assert(drawing == DRAWING_QUADS, "called quads_draw without begin"); - - center.x = x + width/2; - center.y = y + height/2; - center.z = 0; - - vertices[num_vertices].pos.x = x; - vertices[num_vertices].pos.y = y; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - rotate(¢er, &vertices[num_vertices].pos); - - vertices[num_vertices + 1].pos.x = x+width; - vertices[num_vertices + 1].pos.y = y; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - rotate(¢er, &vertices[num_vertices + 1].pos); - - vertices[num_vertices + 2].pos.x = x + width; - vertices[num_vertices + 2].pos.y = y+height; - vertices[num_vertices + 2].tex = texture[2]; - vertices[num_vertices + 2].color = color[2]; - rotate(¢er, &vertices[num_vertices + 2].pos); - - vertices[num_vertices + 3].pos.x = x; - vertices[num_vertices + 3].pos.y = y+height; - vertices[num_vertices + 3].tex = texture[3]; - vertices[num_vertices + 3].color = color[3]; - rotate(¢er, &vertices[num_vertices + 3].pos); - - draw_quad(); -} - -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3) -{ - dbg_assert(drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); - - vertices[num_vertices].pos.x = x0; - vertices[num_vertices].pos.y = y0; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - - vertices[num_vertices + 1].pos.x = x1; - vertices[num_vertices + 1].pos.y = y1; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - - vertices[num_vertices + 2].pos.x = x3; - vertices[num_vertices + 2].pos.y = y3; - vertices[num_vertices + 2].tex = texture[3]; - vertices[num_vertices + 2].color = color[3]; - - vertices[num_vertices + 3].pos.x = x2; - vertices[num_vertices + 3].pos.y = y2; - vertices[num_vertices + 3].tex = texture[2]; - vertices[num_vertices + 3].color = color[2]; - - draw_quad(); -} - -void gfx_quads_text(float x, float y, float size, const char *text) -{ - float startx = x; - - gfx_quads_begin(); - - while(*text) - { - char c = *text; - text++; - - if(c == '\n') - { - x = startx; - y += size; - } - else - { - gfx_quads_setsubset( - (c%16)/16.0f, - (c/16)/16.0f, - (c%16)/16.0f+1.0f/16.0f, - (c/16)/16.0f+1.0f/16.0f); - - gfx_quads_drawTL(x,y,size,size); - x += size/2; - } - } - - gfx_quads_end(); -} - -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++; - } -} - - - -static float pretty_r=1; -static float pretty_g=1; -static float pretty_b=1; -static float pretty_a=1; - -void gfx_pretty_text_color(float r, float g, float b, float a) -{ - pretty_r = r; - pretty_g = g; - pretty_b = b; - pretty_a = a; -} - -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(); - gfx_setcolor(pretty_r, pretty_g, pretty_b, pretty_a); - - if(length < 0) - length = strlen(text_); - - while(length) - { - const int c = *text; - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - double x_nudge = 0; - - text++; - - 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); - - 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; -} - - - -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 unsigned char *stop; - - if (length == -1) - stop = text + strlen((char*)text); - else - stop = text + length; - - while (text < stop) - { - const int c = *text; - const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c]; - - double x_nudge = 0; - if (text[1]) - x_nudge = extra_kerning[text[0] + text[1] * 256]; - - w += (width + spacing + x_nudge) * size; - - text++; - } - - return w; -} - - - -void gfx_lines_begin() -{ - dbg_assert(drawing == 0, "called begin twice"); - drawing = DRAWING_LINES; - gfx_setcolor(1,1,1,1); -} - -void gfx_lines_end() -{ - dbg_assert(drawing == DRAWING_LINES, "called end without begin"); - flush(); - drawing = 0; -} - -void gfx_lines_draw(float x0, float y0, float x1, float y1) -{ - dbg_assert(drawing == DRAWING_LINES, "called draw without begin"); - - vertices[num_vertices].pos.x = x0; - vertices[num_vertices].pos.y = y0; - vertices[num_vertices].tex = texture[0]; - vertices[num_vertices].color = color[0]; - - vertices[num_vertices + 1].pos.x = x1; - vertices[num_vertices + 1].pos.y = y1; - vertices[num_vertices + 1].tex = texture[1]; - vertices[num_vertices + 1].color = color[1]; - - draw_line(); -} diff --git a/src/engine/client/inp.c b/src/engine/client/inp.c deleted file mode 100644 index 7ffb7f3f..00000000 --- a/src/engine/client/inp.c +++ /dev/null @@ -1,210 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include -#include -#include - -static int keyboard_state[2][1024]; /* TODO: fix this!! */ -static int keyboard_current = 0; -static int keyboard_first = 1; - - -static struct -{ - unsigned char presses; - unsigned char releases; -} input_count[2][1024] = {{{0}}, {{0}}}; - -static unsigned char input_state[2][1024] = {{0}, {0}}; - -static int input_current = 0; -static unsigned int last_release = 0; -static unsigned int release_delta = -1; - -void inp_mouse_relative(int *x, int *y) -{ - static int last_x = 0, last_y = 0; - static int last_sens = 100.0f; - int nx, ny; - float sens = config.inp_mousesens/100.0f; - - if(last_sens != config.inp_mousesens) - { - last_x = (last_x/(float)last_sens)*(float)config.inp_mousesens; - last_y = (last_y/(float)last_sens)*(float)config.inp_mousesens; - last_sens = config.inp_mousesens; - } - - - glfwGetMousePos(&nx, &ny); - nx *= sens; - ny *= sens; - - *x = nx-last_x; - *y = ny-last_y; - last_x = nx; - last_y = ny; -} - -static char last_c = 0; -static int last_k = 0; - -static void char_callback(int character, int action) -{ - if(action == GLFW_PRESS && character < 256) - last_c = (char)character; -} - -static void key_callback(int key, int action) -{ - if(action == GLFW_PRESS) - last_k = key; - - if(action == GLFW_PRESS) - input_count[input_current^1][key].presses++; - if(action == GLFW_RELEASE) - input_count[input_current^1][key].releases++; - input_state[input_current^1][key] = action; -} - -static void mousebutton_callback(int button, int action) -{ - if(action == GLFW_PRESS) - last_k = KEY_MOUSE_FIRST+button; - - if(action == GLFW_PRESS) - input_count[input_current^1][KEY_MOUSE_FIRST+button].presses++; - if(action == GLFW_RELEASE) - { - if(button == 0) - { - release_delta = time_get() - last_release; - last_release = time_get(); - } - input_count[input_current^1][KEY_MOUSE_FIRST+button].releases++; - } - input_state[input_current^1][KEY_MOUSE_FIRST+button] = action; -} - - -static void mousewheel_callback(int pos) -{ - if(pos > 0) - { - while(pos-- != 0) - { - input_count[input_current^1][KEY_MOUSE_WHEEL_UP].presses++; - input_count[input_current^1][KEY_MOUSE_WHEEL_UP].releases++; - } - } - else if(pos < 0) - { - while(pos++ != 0) - { - input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].presses++; - input_count[input_current^1][KEY_MOUSE_WHEEL_DOWN].releases++; - } - } - glfwSetMouseWheel(0); -} - - -void inp_init() -{ - glfwEnable(GLFW_KEY_REPEAT); - glfwSetCharCallback(char_callback); - glfwSetKeyCallback(key_callback); - glfwSetMouseButtonCallback(mousebutton_callback); - glfwSetMouseWheelCallback(mousewheel_callback); -} - -char inp_last_char() -{ - return last_c; -} - -int inp_last_key() -{ - return last_k; -} - -void inp_clear() -{ - last_k = 0; - last_c = 0; -} - -void inp_mouse_mode_absolute() -{ - glfwEnable(GLFW_MOUSE_CURSOR); -} - -void inp_mouse_mode_relative() -{ - glfwDisable(GLFW_MOUSE_CURSOR); -} - -int inp_mouse_doubleclick() -{ - return release_delta < (time_freq() >> 2); -} - -int inp_key_presses(int key) -{ - return input_count[input_current][key].presses; -} - -int inp_key_releases(int key) -{ - return input_count[input_current][key].releases; -} - -int inp_key_state(int key) -{ - return input_state[input_current][key]; -} - -int inp_key_pressed(int key) { return keyboard_state[keyboard_current][key]; } -int inp_key_was_pressed(int key) { return keyboard_state[keyboard_current^1][key]; } -int inp_key_down(int key) { return inp_key_pressed(key)&&!inp_key_was_pressed(key); } -int inp_button_pressed(int button) { return keyboard_state[keyboard_current][button]; } - -void inp_update() -{ - int i, v; - - /* clear and begin count on the other one */ - mem_zero(&input_count[input_current], sizeof(input_count[input_current])); - memcpy(input_state[input_current], input_state[input_current^1], sizeof(input_state[input_current])); - input_current^=1; - - if(keyboard_first) - { - /* make sure to reset */ - keyboard_first = 0; - inp_update(); - } - - keyboard_current = keyboard_current^1; - for(i = 0; i < KEY_LAST; i++) - { - if (i >= KEY_MOUSE_FIRST) - v = glfwGetMouseButton(i-KEY_MOUSE_FIRST) == GLFW_PRESS ? 1 : 0; - else - v = glfwGetKey(i) == GLFW_PRESS ? 1 : 0; - keyboard_state[keyboard_current][i] = v; - } - - /* handle mouse wheel */ - /* - i = glfwGetMouseWheel(); - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 0; - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 0; - if(w > 0) - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_UP] = 1; - if(w < 0) - keyboard_state[keyboard_current][KEY_MOUSE_WHEEL_DOWN] = 1; - glfwSetMouseWheel(0);*/ -} diff --git a/src/engine/client/snd.c b/src/engine/client/snd.c deleted file mode 100644 index aa155c0c..00000000 --- a/src/engine/client/snd.c +++ /dev/null @@ -1,441 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include - -#include -#include -#include -#include -#include - -enum -{ - NUM_SAMPLES = 512, - NUM_VOICES = 64, - NUM_CHANNELS = 16, - - MAX_FRAMES = 1024 -}; - -typedef struct -{ - short *data; - int num_frames; - int rate; - int channels; - int loop_start; - int loop_end; -} SAMPLE; - -typedef struct -{ - int vol; - int pan; -} CHANNEL; - -typedef struct VOICE_t -{ - SAMPLE *snd; - CHANNEL *channel; - int tick; - int vol; /* 0 - 255 */ - int flags; - int x, y; -} VOICE; - -static SAMPLE samples[NUM_SAMPLES] = { {0} }; -static VOICE voices[NUM_VOICES] = { {0} }; -static CHANNEL channels[NUM_CHANNELS] = { {255, 0} }; - -static LOCK sound_lock = 0; -static int sound_enabled = 0; - -static int center_x = 0; -static int center_y = 0; - -static int mixing_rate = 48000; - -void snd_set_channel(int cid, float vol, float pan) -{ - channels[cid].vol = (int)(vol*255.0f); - channels[cid].pan = (int)(pan*255.0f); /* TODO: this is only on and off right now */ -} - -static int play(int cid, int sid, int flags, float x, float y) -{ - int vid = -1; - int i; - - lock_wait(sound_lock); - - /* search for voice */ - /* TODO: fix this linear search */ - for(i = 0; i < NUM_VOICES; i++) - { - if(!voices[i].snd) - { - vid = i; - break; - } - } - - /* voice found, use it */ - if(vid != -1) - { - voices[vid].snd = &samples[sid]; - voices[vid].channel = &channels[cid]; - voices[vid].tick = 0; - voices[vid].vol = 255; - voices[vid].flags = flags; - voices[vid].x = (int)x; - voices[vid].y = (int)y; - } - - lock_release(sound_lock); - return vid; -} - -int snd_play_at(int cid, int sid, int flags, float x, float y) -{ - return play(cid, sid, flags|SNDFLAG_POS, x, y); -} - -int snd_play(int cid, int sid, int flags) -{ - return play(cid, sid, flags, 0, 0); -} - -void snd_stop(int vid) -{ - /* TODO: a nice fade out */ - lock_wait(sound_lock); - voices[vid].snd = 0; - lock_release(sound_lock); -} - -/* TODO: there should be a faster way todo this */ -static short int2short(int i) -{ - if(i > 0x7fff) - return 0x7fff; - else if(i < -0x7fff) - return -0x7fff; - return i; -} - -static int iabs(int i) -{ - if(i<0) - return -i; - return i; -} - -static void mix(short *final_out, unsigned frames) -{ - int mix_buffer[MAX_FRAMES*2] = {0}; - int i, s; - - /* aquire lock while we are mixing */ - lock_wait(sound_lock); - - for(i = 0; i < NUM_VOICES; i++) - { - if(voices[i].snd) - { - /* mix voice */ - VOICE *v = &voices[i]; - int *out = mix_buffer; - - int step = v->snd->channels; /* setup input sources */ - short *in_l = &v->snd->data[v->tick*step]; - short *in_r = &v->snd->data[v->tick*step+1]; - - int end = v->snd->num_frames-v->tick; - - int rvol = v->channel->vol; - int lvol = v->channel->vol; - - /* make sure that we don't go outside the sound data */ - if(frames < end) - end = frames; - - /* check if we have a mono sound */ - if(v->snd->channels == 1) - in_r = in_l; - - /* volume calculation */ - if(v->flags&SNDFLAG_POS && v->channel->pan) - { - /* TODO: we should respect the channel panning value */ - const int range = 1500; /* magic value, remove */ - int dx = v->x - center_x; - int dy = v->y - center_y; - int dist = sqrt(dx*dx+dy*dy); /* double here. nasty */ - int p = iabs(dx); - if(dist < range) - { - /* panning */ - if(dx > 0) - lvol = ((range-p)*lvol)/range; - else - rvol = ((range-p)*rvol)/range; - - /* falloff */ - lvol = (lvol*(range-dist))/range; - rvol = (rvol*(range-dist))/range; - } - else - { - lvol = 0; - rvol = 0; - } - } - - /* process all frames */ - for(s = 0; s < end; s++) - { - *out++ += (*in_l)*lvol; - *out++ += (*in_r)*rvol; - in_l += step; - in_r += step; - v->tick++; - } - - /* free voice if not used any more */ - if(v->tick == v->snd->num_frames) - v->snd = 0; - - } - } - - /* release the lock */ - lock_release(sound_lock); - - { - int master_vol = config.snd_volume; - - /* clamp accumulated values */ - /* TODO: this seams slow */ - for(i = 0; i < frames; i++) - { - int j = i<<1; - int vl = ((mix_buffer[j]*master_vol)/101)>>8; - int vr = ((mix_buffer[j+1]*master_vol)/101)>>8; - - final_out[j] = int2short(vl); - final_out[j+1] = int2short(vr); - } - } -} - -static int pacallback(const void *in, void *out, unsigned long frames, const PaStreamCallbackTimeInfo* time, PaStreamCallbackFlags status, void *user) -{ - mix(out, frames); - return 0; -} - -static PaStream *stream; - -int snd_init() -{ - PaStreamParameters params; - PaError err = Pa_Initialize(); - - sound_lock = lock_create(); - - if(!config.snd_enable) - return 0; - - mixing_rate = config.snd_rate; - - params.device = Pa_GetDefaultOutputDevice(); - if(params.device < 0) - return 1; - params.channelCount = 2; - params.sampleFormat = paInt16; - params.suggestedLatency = Pa_GetDeviceInfo(params.device)->defaultLowOutputLatency; - params.hostApiSpecificStreamInfo = 0x0; - - err = Pa_OpenStream( - &stream, /* passes back stream pointer */ - 0, /* no input channels */ - ¶ms, /* pointer to parameters */ - mixing_rate, /* sample rate */ - 128, /* frames per buffer */ - paClipOff, /* no clamping */ - pacallback, /* specify our custom callback */ - 0x0); /* pass our data through to callback */ - err = Pa_StartStream(stream); - - sound_enabled = 1; - return 0; -} - -int snd_shutdown() -{ - Pa_StopStream(stream); - Pa_Terminate(); - - lock_destroy(sound_lock); - - return 0; -} - -int snd_alloc_id() -{ - /* TODO: linear search, get rid of it */ - unsigned sid; - for(sid = 0; sid < NUM_SAMPLES; sid++) - { - if(samples[sid].data == 0x0) - return sid; - } - - return -1; -} - -static void rate_convert(int sid) -{ - SAMPLE *snd = &samples[sid]; - int num_frames = 0; - short *new_data = 0; - int i; - - /* make sure that we need to convert this sound */ - if(!snd->data || snd->rate == mixing_rate) - return; - - /* allocate new data */ - num_frames = (int)((snd->num_frames/(float)snd->rate)*mixing_rate); - new_data = mem_alloc(num_frames*snd->channels*sizeof(short), 1); - - for(i = 0; i < num_frames; i++) - { - /* resample TODO: this should be done better, like linear atleast */ - float a = i/(float)num_frames; - int f = (int)(a*snd->num_frames); - if(f >= snd->num_frames) - f = snd->num_frames-1; - - /* set new data */ - if(snd->channels == 1) - new_data[i] = snd->data[f]; - else if(snd->channels == 2) - { - new_data[i*2] = snd->data[f*2]; - new_data[i*2+1] = snd->data[f*2+1]; - } - } - - /* free old data and apply new */ - mem_free(snd->data); - snd->data = new_data; - snd->num_frames = num_frames; -} - - -static FILE *file = NULL; - -static int read_data(void *buffer, int size) -{ - return fread(buffer, 1, size, file); -} - -int snd_load_wv(const char *filename) -{ - SAMPLE *snd; - int sid = -1; - char error[100]; - WavpackContext *context; - - /* don't waste memory on sound when we are stress testing */ - if(config.dbg_stress) - return -1; - - /* no need to load sound when we are running with no sound */ - if(!sound_enabled) - return 1; - - file = fopen(filename, "rb"); /* TODO: use system.h stuff for this */ - if(!file) - { - dbg_msg("sound/wv", "failed to open %s", filename); - return -1; - } - - sid = snd_alloc_id(); - if(sid < 0) - return -1; - snd = &samples[sid]; - - context = WavpackOpenFileInput(read_data, error); - if (context) - { - int samples = WavpackGetNumSamples(context); - int bitspersample = WavpackGetBitsPerSample(context); - unsigned int samplerate = WavpackGetSampleRate(context); - int channels = WavpackGetNumChannels(context); - int *data; - int *src; - short *dst; - int i; - - snd->channels = channels; - snd->rate = samplerate; - - if(snd->channels > 2) - { - dbg_msg("sound/wv", "file is not mono or stereo. filename='%s'", filename); - return -1; - } - - /* - if(snd->rate != 44100) - { - dbg_msg("sound/wv", "file is %d Hz, not 44100 Hz. filename='%s'", snd->rate, filename); - return -1; - }*/ - - if(bitspersample != 16) - { - dbg_msg("sound/wv", "bps is %d, not 16, filname='%s'", bitspersample, filename); - return -1; - } - - data = (int *)mem_alloc(4*samples*channels, 1); - WavpackUnpackSamples(context, data, samples); /* TODO: check return value */ - src = data; - - snd->data = (short *)mem_alloc(2*samples*channels, 1); - dst = snd->data; - - for (i = 0; i < samples*channels; i++) - *dst++ = (short)*src++; - - mem_free(data); - - snd->num_frames = samples; - snd->loop_start = -1; - snd->loop_end = -1; - } - else - { - dbg_msg("sound/wv", "failed to open %s: %s", filename, error); - } - - fclose(file); - file = NULL; - - if(config.debug) - dbg_msg("sound/wv", "loaded %s", filename); - - rate_convert(sid); - return sid; -} - -void snd_set_listener_pos(float x, float y) -{ - center_x = (int)x; - center_y = (int)y; -} diff --git a/src/engine/client/srvbrowse.c b/src/engine/client/srvbrowse.c deleted file mode 100644 index 9fbbc2a0..00000000 --- a/src/engine/client/srvbrowse.c +++ /dev/null @@ -1,404 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include - -#include - -#include -#include - -extern NETCLIENT *net; - - -/* ------ server browse ---- */ -/* TODO: move all this to a separate file */ - -typedef struct SERVERENTRY_t SERVERENTRY; -struct SERVERENTRY_t -{ - NETADDR4 addr; - int64 request_time; - int got_info; - SERVER_INFO info; - - SERVERENTRY *next_ip; /* ip hashed list */ - - SERVERENTRY *prev_req; /* request list */ - SERVERENTRY *next_req; -}; - -static HEAP *serverlist_heap = 0; -static SERVERENTRY **serverlist = 0; -static int *sorted_serverlist = 0; - -static SERVERENTRY *serverlist_ip[256] = {0}; /* ip hash list */ - -static SERVERENTRY *first_req_server = 0; /* request list */ -static SERVERENTRY *last_req_server = 0; -static int num_requests = 0; - -static int num_sorted_servers = 0; -static int num_sorted_servers_capacity = 0; -static int num_servers = 0; -static int num_server_capacity = 0; - -static int sorthash = 0; -static char filterstring[64] = {0}; - -static int serverlist_lan = 1; - -int client_serverbrowse_num() { return num_servers; } - -SERVER_INFO *client_serverbrowse_get(int index) -{ - if(index < 0 || index >= num_servers) - return 0; - return &serverlist[index]->info; -} - -int client_serverbrowse_sorted_num() { return num_sorted_servers; } - -SERVER_INFO *client_serverbrowse_sorted_get(int index) -{ - if(index < 0 || index >= num_sorted_servers) - return 0; - return &serverlist[sorted_serverlist[index]]->info; -} - - -int client_serverbrowse_num_requests() -{ - return num_requests; -} - -static int client_serverbrowse_sort_compare_name(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.name, b->info.name); -} - -static int client_serverbrowse_sort_compare_map(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return strcmp(a->info.map, b->info.map); -} - -static int client_serverbrowse_sort_compare_ping(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.latency > b->info.latency; -} - -static int client_serverbrowse_sort_compare_gametype(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.game_type > b->info.game_type; -} - -static int client_serverbrowse_sort_compare_progression(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.progression > b->info.progression; -} - -static int client_serverbrowse_sort_compare_numplayers(const void *ai, const void *bi) -{ - SERVERENTRY *a = serverlist[*(const int*)ai]; - SERVERENTRY *b = serverlist[*(const int*)bi]; - return a->info.num_players > b->info.num_players; -} - -static void client_serverbrowse_filter() -{ - int i = 0; - num_sorted_servers = 0; - - /* allocate the sorted list */ - if(num_sorted_servers_capacity < num_servers) - { - if(sorted_serverlist) - mem_free(sorted_serverlist); - num_sorted_servers_capacity = num_servers; - sorted_serverlist = mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); - } - - /* filter the servers */ - for(i = 0; i < num_servers; i++) - { - int filtered = 0; - - if(config.b_filter_empty && serverlist[i]->info.num_players == 0) - filtered = 1; - else if(config.b_filter_full && serverlist[i]->info.num_players == serverlist[i]->info.max_players) - filtered = 1; - else if(config.b_filter_pw && serverlist[i]->info.flags&1) - filtered = 1; - else if(config.b_filter_string[0] != 0) - { - if(strstr(serverlist[i]->info.name, config.b_filter_string) == 0) - filtered = 1; - } - - if(filtered == 0) - sorted_serverlist[num_sorted_servers++] = i; - } -} - -static int client_serverbrowse_sorthash() -{ - int i = config.b_sort&0xf; - i |= config.b_filter_empty<<4; - i |= config.b_filter_full<<5; - i |= config.b_filter_pw<<6; - return i; -} - -static void client_serverbrowse_sort() -{ - int i; - - /* create filtered list */ - client_serverbrowse_filter(); - - /* sort */ - if(config.b_sort == BROWSESORT_NAME) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_name); - else if(config.b_sort == BROWSESORT_PING) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_ping); - else if(config.b_sort == BROWSESORT_MAP) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_map); - else if(config.b_sort == BROWSESORT_NUMPLAYERS) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_numplayers); - else if(config.b_sort == BROWSESORT_GAMETYPE) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_gametype); - else if(config.b_sort == BROWSESORT_PROGRESSION) - qsort(sorted_serverlist, num_sorted_servers, sizeof(int), client_serverbrowse_sort_compare_progression); - - /* set indexes */ - for(i = 0; i < num_sorted_servers; i++) - serverlist[sorted_serverlist[i]]->info.sorted_index = i; - - strncpy(filterstring, config.b_filter_string, sizeof(filterstring)-1); - sorthash = client_serverbrowse_sorthash(); -} - -static void client_serverbrowse_remove_request(SERVERENTRY *entry) -{ - if(entry->prev_req || entry->next_req || first_req_server == entry) - { - if(entry->prev_req) - entry->prev_req->next_req = entry->next_req; - else - first_req_server = entry->next_req; - - if(entry->next_req) - entry->next_req->prev_req = entry->prev_req; - else - last_req_server = entry->prev_req; - - entry->prev_req = 0; - entry->next_req = 0; - num_requests--; - } -} - -void client_serverbrowse_set(NETADDR4 *addr, int request, SERVER_INFO *info) -{ - int hash = addr->ip[0]; - SERVERENTRY *entry = serverlist_ip[hash]; - while(entry) - { - if(net_addr4_cmp(&entry->addr, addr) == 0) - { - /* update the server that we already have */ - entry->info = *info; - if(!request) - { - entry->info.latency = (time_get()-entry->request_time)*1000/time_freq(); - client_serverbrowse_remove_request(entry); - } - - entry->got_info = 1; - client_serverbrowse_sort(); - return; - } - entry = entry->next_ip; - } - - /* create new entry */ - entry = (SERVERENTRY *)memheap_allocate(serverlist_heap, sizeof(SERVERENTRY)); - mem_zero(entry, sizeof(SERVERENTRY)); - - /* set the info */ - entry->addr = *addr; - entry->info = *info; - - /* add to the hash list */ - entry->next_ip = serverlist_ip[hash]; - serverlist_ip[hash] = entry; - - if(num_servers == num_server_capacity) - { - SERVERENTRY **newlist; - num_server_capacity += 100; - newlist = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); - memcpy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); - mem_free(serverlist); - serverlist = newlist; - } - - /* add to list */ - serverlist[num_servers] = entry; - entry->info.server_index = num_servers; - num_servers++; - - /* */ - if(request) - { - /* add it to the list of servers that we should request info from */ - entry->prev_req = last_req_server; - if(last_req_server) - last_req_server->next_req = entry; - else - first_req_server = entry; - last_req_server = entry; - - num_requests++; - } - - client_serverbrowse_sort(); -} - -void client_serverbrowse_refresh(int lan) -{ - /* clear out everything */ - if(serverlist_heap) - memheap_destroy(serverlist_heap); - serverlist_heap = memheap_create(); - num_servers = 0; - num_sorted_servers = 0; - mem_zero(serverlist_ip, sizeof(serverlist_ip)); - first_req_server = 0; - last_req_server = 0; - num_requests = 0; - - - /* */ - serverlist_lan = lan; - - if(serverlist_lan) - { - NETPACKET packet; - packet.client_id = -1; - mem_zero(&packet, sizeof(packet)); - packet.address.ip[0] = 255; - packet.address.ip[1] = 255; - packet.address.ip[2] = 255; - packet.address.ip[3] = 255; - packet.address.port = 8303; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_GETINFO); - packet.data = SERVERBROWSE_GETINFO; - netclient_send(net, &packet); - - if(config.debug) - dbg_msg("client", "broadcasting for servers"); - } - else - { - NETADDR4 master_server; - NETPACKET p; - - net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server); - - mem_zero(&p, sizeof(p)); - p.client_id = -1; - p.address = master_server; - p.flags = PACKETFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_GETLIST); - p.data = SERVERBROWSE_GETLIST; - netclient_send(net, &p); - - if(config.debug) - dbg_msg("client", "requesting server list"); - } -} - -static void client_serverbrowse_request(SERVERENTRY *entry) -{ - NETPACKET p; - - if(config.debug) - { - dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", - entry->addr.ip[0], entry->addr.ip[1], entry->addr.ip[2], - entry->addr.ip[3], entry->addr.port); - } - - p.client_id = -1; - p.address = entry->addr; - p.flags = PACKETFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_GETINFO); - p.data = SERVERBROWSE_GETINFO; - netclient_send(net, &p); - entry->request_time = time_get(); -} - -void client_serverbrowse_update() -{ - int64 timeout = time_freq(); - int64 now = time_get(); - int count; - SERVERENTRY *entry, *next; - - /* do timeouts */ - entry = first_req_server; - while(1) - { - if(!entry) /* no more entries */ - break; - - next = entry->next_req; - - if(entry->request_time && entry->request_time+timeout < now) - { - /* timeout */ - client_serverbrowse_remove_request(entry); - num_requests--; - } - - entry = next; - } - - /* do timeouts */ - entry = first_req_server; - count = 0; - while(1) - { - if(!entry) /* no more entries */ - break; - - /* no more then 10 concurrent requests */ - if(count == config.b_max_requests) - break; - - if(entry->request_time == 0) - client_serverbrowse_request(entry); - - count++; - entry = entry->next_req; - } - - /* check if we need to resort */ - /* TODO: remove the strcmp */ - if(sorthash != client_serverbrowse_sorthash() || strcmp(filterstring, config.b_filter_string) != 0) - client_serverbrowse_sort(); -} diff --git a/src/engine/client/ui.c b/src/engine/client/ui.c deleted file mode 100644 index 9486b2c1..00000000 --- a/src/engine/client/ui.c +++ /dev/null @@ -1,277 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include "ui.h" - -/******************************************************** - UI -*********************************************************/ - -struct pretty_font -{ - float m_CharStartTable[256]; - float m_CharEndTable[256]; - int font_texture; -}; - -extern struct pretty_font *current_font; - -static const void *hot_item = 0; -static const void *active_item = 0; -static const void *last_active_item = 0; -static const void *becomming_hot_item = 0; -static float mouse_x, mouse_y; /* in gui space */ -static float mouse_wx, mouse_wy; /* in world space */ -static unsigned mouse_buttons = 0; - -float ui_mouse_x() { return mouse_x; } -float ui_mouse_y() { return mouse_y; } -float ui_mouse_world_x() { return mouse_wx; } -float ui_mouse_world_y() { return mouse_wy; } -int ui_mouse_button(int index) { return (mouse_buttons>>index)&1; } - -void ui_set_hot_item(const void *id) { becomming_hot_item = id; } -void ui_set_active_item(const void *id) { active_item = id; if (id) last_active_item = id; } -void ui_clear_last_active_item() { last_active_item = 0; } -const void *ui_hot_item() { return hot_item; } -const void *ui_active_item() { return active_item; } -const void *ui_last_active_item() { return last_active_item; } - -int ui_update(float mx, float my, float mwx, float mwy, int buttons) -{ - mouse_x = mx; - mouse_y = my; - mouse_wx = mwx; - mouse_wy = mwy; - mouse_buttons = buttons; - hot_item = becomming_hot_item; - if(active_item) - hot_item = active_item; - becomming_hot_item = 0; - return 0; -} - -int ui_mouse_inside(float x, float y, float w, float h) -{ - if(mouse_x >= x && mouse_x <= x+w && mouse_y >= y && mouse_y <= y+h) - return 1; - return 0; -} - -void ui_do_image(int texture, float x, float y, float w, float h) -{ - gfx_blend_normal(); - gfx_texture_set(texture); - gfx_quads_begin(); - gfx_setcolor(1,1,1,1); - gfx_quads_setsubset( - 0.0f, /* startx */ - 0.0f, /* starty */ - 1.0f, /* endx */ - 1.0f); /* endy */ - gfx_quads_drawTL(x,y,w,h); - gfx_quads_end(); -} - -void ui_do_label(float x, float y, const char *text, float size) -{ - gfx_blend_normal(); - gfx_texture_set(current_font->font_texture); - gfx_pretty_text(x, y, size, text, -1); -} - -int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra) -{ - /* logic */ - int r = 0; - int inside = ui_mouse_inside(x,y,w,h); - - if(ui_active_item() == id) - { - if(!ui_mouse_button(0)) - { - if(inside) - r = 1; - ui_set_active_item(0); - } - } - else if(ui_hot_item() == id) - { - if(ui_mouse_button(0)) - ui_set_active_item(id); - } - - if(inside) - ui_set_hot_item(id); - - draw_func(id, text, checked, x, y, w, h, extra); - return r; -} - -static float scale = 1.0f; -#define MEMORY_SIZE 10*1024 -static struct rect memory[MEMORY_SIZE]; -static int memory_used = 0; -static struct rect screen = { 0.0f, 0.0f, 848.0f, 480.0f }; - -void ui_foreach_rect(rect_fun fun) -{ - int hrm; - for (hrm = 0; hrm < memory_used; hrm++) - fun(&memory[hrm]); -} - -static void add_rect(struct rect *r) -{ - if (memory_used < MEMORY_SIZE) - memory[memory_used++] = *r; - else - dbg_msg("ui", "rect memory full."); -} - -struct rect *ui_screen() -{ - if (config.debug) - { - memory_used = 0; - } - - return &screen; -} - -void ui_scale(float s) -{ - scale = s; -} - -void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) -{ - struct rect r = *original; - pixels *= scale; - - if (top) - { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = pixels; - } - - if (bottom) - { - bottom->x = r.x; - bottom->y = r.y + pixels; - bottom->w = r.w; - bottom->h = r.h - pixels; - } - - if (config.debug) - { - if (top) - add_rect(top); - if (bottom) - add_rect(bottom); - } -} - -void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom) -{ - struct rect r = *original; - pixels *= scale; - - if (top) - { - top->x = r.x; - top->y = r.y; - top->w = r.w; - top->h = r.h - pixels; - } - - if (bottom) - { - bottom->x = r.x; - bottom->y = r.y + r.h - pixels; - bottom->w = r.w; - bottom->h = pixels; - } - - if (config.debug) - { - if (top) - add_rect(top); - if (bottom) - add_rect(bottom); - } -} - -void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right) -{ - struct rect r = *original; - pixels *= scale; - - if (left) - { - left->x = r.x; - left->y = r.y; - left->w = pixels; - left->h = r.h; - } - - if (right) - { - right->x = r.x + pixels; - right->y = r.y; - right->w = r.w - pixels; - right->h = r.h; - } - - if (config.debug) - { - if (left) - add_rect(left); - if (right) - add_rect(right); - } -} - -void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right) -{ - struct rect r = *original; - pixels *= scale; - - if (left) - { - left->x = r.x; - left->y = r.y; - left->w = r.w - pixels; - left->h = r.h; - } - - if (right) - { - right->x = r.x + r.w - pixels; - right->y = r.y; - right->w = pixels; - right->h = r.h; - } - - if (config.debug) - { - if (left) - add_rect(left); - if (right) - add_rect(right); - } -} - -void ui_margin(const struct rect *original, int pixels, struct rect *other_rect) -{ - struct rect r = *original; - pixels *= scale; - - other_rect->x = r.x + pixels; - other_rect->y = r.y + pixels; - other_rect->w = r.w - 2*pixels; - other_rect->h = r.h - 2*pixels; -} diff --git a/src/engine/client/ui.h b/src/engine/client/ui.h deleted file mode 100644 index 15c63b90..00000000 --- a/src/engine/client/ui.h +++ /dev/null @@ -1,36 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef _UI_H -#define _UI_H - -#ifdef __cplusplus -extern "C" { -#endif - -int ui_update(float mx, float my, float mwx, float mwy, int buttons); - -float ui_mouse_x(); -float ui_mouse_y(); -float ui_mouse_world_x(); -float ui_mouse_world_y(); -int ui_mouse_button(int index); - -void ui_set_hot_item(const void *id); -void ui_set_active_item(const void *id); -void ui_clear_last_active_item(); -const void *ui_hot_item(); -const void *ui_active_item(); -const void *ui_last_active_item(); - -int ui_mouse_inside(float x, float y, float w, float h); - -typedef void (*draw_button_callback)(const void *id, const char *text, int checked, float x, float y, float w, float h, void *extra); - -void ui_do_image(int texture, float x, float y, float w, float h); -void ui_do_label(float x, float y, const char *text, float size); -int ui_do_button(const void *id, const char *text, int checked, float x, float y, float w, float h, draw_button_callback draw_func, void *extra); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/engine/compression.c b/src/engine/compression.c deleted file mode 100644 index d99624c0..00000000 --- a/src/engine/compression.c +++ /dev/null @@ -1,147 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" -#include - -/* Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign */ -unsigned char *vint_pack(unsigned char *dst, int i) -{ - *dst = (i>>25)&0x40; /* set sign bit if i<0 */ - i = i^(i>>31); /* if(i<0) i = ~i */ - - *dst |= i&0x3F; /* pack 6bit into dst */ - i >>= 6; /* discard 6 bits */ - if(i) - { - *dst |= 0x80; /* set extend bit */ - while(1) - { - dst++; - *dst = i&(0x7F); /* pack 7bit */ - i >>= 7; /* discard 7 bits */ - *dst |= (i!=0)<<7; /* set extend bit (may branch) */ - if(!i) - break; - } - } - - dst++; - return dst; -} - -const unsigned char *vint_unpack(const unsigned char *src, int *i) -{ - int sign = (*src>>6)&1; - *i = *src&0x3F; - - do - { - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7+7); - - if(!(*src&0x80)) break; - src++; - *i |= (*src&(0x7F))<<(6+7+7+7); - } while(0); - - src++; - *i ^= -sign; /* if(sign) *i = ~(*i) */ - return src; -} - - -long intpack_decompress(const void *src_, int size, void *dst_) -{ - const unsigned char *src = (unsigned char *)src_; - const unsigned char *end = src + size; - int *dst = (int *)dst_; - while(src < end) - { - src = vint_unpack(src, dst); - dst++; - } - return (long)((unsigned char *)dst-(unsigned char *)dst_); -} - -long intpack_compress(const void *src_, int size, void *dst_) -{ - int *src = (int *)src_; - unsigned char *dst = (unsigned char *)dst_; - size /= 4; - while(size) - { - dst = vint_pack(dst, *src); - size--; - src++; - } - return (long)(dst-(unsigned char *)dst_); -} - - -/* */ -long zerobit_compress(const void *src_, int size, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *dst = (unsigned char *)dst_; - - while(size) - { - unsigned char bit = 0x80; - unsigned char mask = 0; - int dst_move = 1; - int chunk = size < 8 ? size : 8; - int b; - size -= chunk; - - for(b = 0; b < chunk; b++, bit>>=1) - { - if(*src) - { - dst[dst_move] = *src; - mask |= bit; - dst_move++; - } - - src++; - } - - *dst = mask; - dst += dst_move; - } - - return (long)(dst-(unsigned char *)dst_); -} - -long zerobit_decompress(const void *src_, int size, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *dst = (unsigned char *)dst_; - unsigned char *end = src + size; - - - while(src != end) - { - unsigned char bit = 0x80; - unsigned char mask = *src++; - int b; - - for(b = 0; b < 8; b++, bit>>=1) - { - if(mask&bit) - *dst++ = *src++; - else - *dst++ = 0; - } - } - - return (long)(dst-(unsigned char *)dst_); -} - diff --git a/src/engine/compression.h b/src/engine/compression.h deleted file mode 100644 index 39ffd596..00000000 --- a/src/engine/compression.h +++ /dev/null @@ -1,11 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -/* variable int packing */ -unsigned char *vint_pack(unsigned char *dst, int i); -const unsigned char *vint_unpack(const unsigned char *src, int *inout); -long intpack_compress(const void *src, int size, void *dst); -long intpack_decompress(const void *src, int size, void *dst); - -/* zerobit packing */ -long zerobit_compress(const void *src, int size, void *dst); -long zerobit_decompress(const void *src, int size, void *dst); diff --git a/src/engine/config.c b/src/engine/config.c deleted file mode 100644 index 3fa6f98d..00000000 --- a/src/engine/config.c +++ /dev/null @@ -1,196 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include -#include -#include - -#include "system.h" -#include "config.h" - - -/* buffered stream for reading lines, should perhaps be something smaller */ -typedef struct -{ - char buffer[4*1024]; - unsigned buffer_pos; - unsigned buffer_size; - unsigned buffer_max_size; - IOHANDLE io; -} LINEREADER; - -void linereader_init(LINEREADER *lr, IOHANDLE io) -{ - lr->buffer_max_size = 4*1024; - lr->buffer_size = 0; - lr->buffer_pos = 0; - lr->io = io; -} - -char *linereader_get(LINEREADER *lr) -{ - unsigned line_start = lr->buffer_pos; - - while(1) - { - if(lr->buffer_pos >= lr->buffer_size) - { - /* fetch more */ - - /* move the remaining part to the front */ - unsigned read; - unsigned left = lr->buffer_size - line_start; - - if(line_start > lr->buffer_size) - left = 0; - if(left) - mem_move(lr->buffer, &lr->buffer[line_start], left); - lr->buffer_pos = left; - - /* fill the buffer */ - read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos); - lr->buffer_size = left + read; - line_start = 0; - - if(!read) - { - if(left) - { - lr->buffer[left] = 0; /* return the last line */ - lr->buffer_pos = left; - lr->buffer_size = left; - return lr->buffer; - } - else - return 0x0; /* we are done! */ - } - } - else - { - if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r') - { - /* line found */ - lr->buffer[lr->buffer_pos] = 0; - lr->buffer_pos++; - return &lr->buffer[line_start]; - } - else - lr->buffer_pos++; - } - } -} - -CONFIGURATION config; - -void config_reset() -{ - #define MACRO_CONFIG_INT(name,def,min,max) config.name = def; - #define MACRO_CONFIG_STR(name,len,def) strncpy(config.name, def, len); - - #include "config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} - -void strip_spaces(char **p) -{ - char *s = *p; - char *end; - - while (*s == ' ') - ++s; - - end = s + strlen(s); - while (end > s && *(end - 1) == ' ') - *--end = 0; -} - -void config_set(const char *line) -{ - const char *c = strchr(line, '='); - if (c) - { - char var[256]; - char val[256]; - char *var_str = var; - char *val_str = val; - - strcpy(val, c+1); - - mem_copy(var, line, c - line); - var[c - line] = 0; - - - strip_spaces(&var_str); - strip_spaces(&val_str); - - #define MACRO_CONFIG_INT(name,def,min,max) { if (strcmp(#name, var_str) == 0) config_set_ ## name (&config, atoi(val_str)); } - #define MACRO_CONFIG_STR(name,len,def) { if (strcmp(#name, var_str) == 0) { config_set_ ## name (&config, val_str); } } - - #include "config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR - } -} - -void config_load(const char *filename) -{ - IOHANDLE file; - dbg_msg("config/load", "loading %s", filename); - file = io_open(filename, IOFLAG_READ); - - if(file) - { - char *line; - LINEREADER lr; - linereader_init(&lr, file); - - while ((line = linereader_get(&lr))) - config_set(line); - - io_close(file); - } -} - -void config_save(const char *filename) -{ - IOHANDLE file; - dbg_msg("config/save", "saving config to %s", filename); - - file = io_open(filename, IOFLAG_WRITE); - - if(file) - { -#if defined(CONF_FAMILY_WINDOWS) - const char newline[] = "\r\n"; -#else - const char newline[] = "\n"; -#endif - const int newline_len = sizeof(newline)-1; - - #define MACRO_CONFIG_INT(name,def,min,max) { char str[256]; sprintf(str, "%s=%i%s", #name, config.name, newline); io_write(file, str, strlen(str)); } - #define MACRO_CONFIG_STR(name,len,def) { io_write(file, #name, strlen(#name)); io_write(file, "=", 1); io_write(file, config.name, strlen(config.name)); io_write(file, newline, newline_len); } - - #include "config_variables.h" - - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR - - io_close(file); - } - else - dbg_msg("config/save", "couldn't open %s for writing. :(", filename); -} - -#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c) { return c->name; } -#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; } -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; } -#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str) { strncpy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; } -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR diff --git a/src/engine/config.h b/src/engine/config.h deleted file mode 100644 index e01a7ec8..00000000 --- a/src/engine/config.h +++ /dev/null @@ -1,41 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef _CONFIG_H -#define _CONFIG_H - -#ifdef __cplusplus -extern "C"{ -#endif - -typedef struct -{ - #define MACRO_CONFIG_INT(name,def,min,max) int name; - #define MACRO_CONFIG_STR(name,len,def) char name[len]; - #include "config_variables.h" - #undef MACRO_CONFIG_INT - #undef MACRO_CONFIG_STR -} CONFIGURATION; - -extern CONFIGURATION config; - -void config_set(const char *line); -void config_reset(); -void config_load(const char *filename); -void config_save(const char *filename); - -#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c); -#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c); -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val); -#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str); -#include "config_variables.h" -#undef MACRO_CONFIG_INT -#undef MACRO_CONFIG_STR - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/engine/config_variables.h b/src/engine/config_variables.h deleted file mode 100644 index 4a63e731..00000000 --- a/src/engine/config_variables.h +++ /dev/null @@ -1,56 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "../game/game_variables.h" - - -MACRO_CONFIG_STR(player_name, 32, "nameless tee") -MACRO_CONFIG_STR(clan_name, 32, "") -MACRO_CONFIG_STR(password, 32, "") -MACRO_CONFIG_STR(rcon_password, 32, "") - -MACRO_CONFIG_INT(cl_cpu_throttle, 0, 0, 1) -/*MACRO_CONFIG_STR(cl_connect, 32, "")*/ -MACRO_CONFIG_INT(cl_editor, 0, 0, 1) - -MACRO_CONFIG_STR(b_filter_string, 64, "") - -MACRO_CONFIG_INT(b_filter_full, 0, 0, 1) -MACRO_CONFIG_INT(b_filter_empty, 0, 0, 1) -MACRO_CONFIG_INT(b_filter_pw, 0, 0, 1) -MACRO_CONFIG_INT(b_sort, 0, 0, 256) -MACRO_CONFIG_INT(b_max_requests, 10, 0, 1000) - -MACRO_CONFIG_INT(snd_rate, 48000, 0, 0) -MACRO_CONFIG_INT(snd_enable, 1, 0, 1) -MACRO_CONFIG_INT(snd_volume, 100, 0, 100) - -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) -MACRO_CONFIG_INT(gfx_color_depth, 24, 16, 24) -MACRO_CONFIG_INT(gfx_vsync, 1, 0, 1) -MACRO_CONFIG_INT(gfx_display_all_modes, 0, 0, 1) -MACRO_CONFIG_INT(gfx_texture_compression, 0, 0, 1) -MACRO_CONFIG_INT(gfx_high_detail, 1, 0, 1) -MACRO_CONFIG_INT(gfx_texture_quality, 1, 0, 1) -MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16) -MACRO_CONFIG_INT(gfx_refresh_rate, 0, 0, 0) - -MACRO_CONFIG_INT(key_screenshot, 267, 32, 512) -MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000) - -MACRO_CONFIG_STR(masterserver, 128, "master.teewars.com") - -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_external_port, 0, 0, 0) -MACRO_CONFIG_INT(sv_sendheartbeats, 1, 0, 1) -MACRO_CONFIG_STR(sv_map, 128, "dm1") -MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1) -MACRO_CONFIG_INT(sv_max_clients, 8, 1, 12) -MACRO_CONFIG_INT(sv_bandwidth_mode, 0, 0, 2) - -MACRO_CONFIG_INT(debug, 0, 0, 1) -MACRO_CONFIG_INT(dbg_stress, 0, 0, 0) -MACRO_CONFIG_STR(dbg_stress_server, 32, "localhost") - diff --git a/src/engine/datafile.c b/src/engine/datafile.c deleted file mode 100644 index 7069044c..00000000 --- a/src/engine/datafile.c +++ /dev/null @@ -1,630 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" -#include "datafile.h" -#include "external/zlib/zlib.h" - -static const int DEBUG=0; - -typedef struct -{ - int type; - int start; - int num; -} DATAFILE_ITEM_TYPE; - -typedef struct -{ - int type_and_id; - int size; -} DATAFILE_ITEM; - -typedef struct -{ - char id[4]; - int version; - int size; - int swaplen; - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; -} DATAFILE_HEADER; - -typedef struct -{ - int num_item_types; - int num_items; - int num_raw_data; - int item_size; - int data_size; - char start[4]; -} DATAFILE_DATA; - -typedef struct -{ - DATAFILE_ITEM_TYPE *item_types; - int *item_offsets; - int *data_offsets; - int *data_sizes; - - char *item_start; - char *data_start; -} DATAFILE_INFO; - -struct DATAFILE_t -{ - IOHANDLE file; - DATAFILE_INFO info; - DATAFILE_HEADER header; - int data_start_offset; - char **data_ptrs; - char *data; -}; - -DATAFILE *datafile_load(const char *filename) -{ - DATAFILE *df; - IOHANDLE file; - DATAFILE_HEADER header; - unsigned readsize; - - unsigned *dst; - unsigned char *src; - unsigned j; - int size = 0; - int allocsize = 0; - - (void)dst; - (void)src; - (void)j; - - - dbg_msg("datafile", "datafile loading. filename='%s'", filename); - - file = io_open(filename, IOFLAG_READ); - if(!file) - return 0; - - /* TODO: change this header */ - io_read(file, &header, sizeof(header)); - if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D') - { - if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A') - { - dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]); - return 0; - } - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int)); -#endif - if(header.version != 3 && header.version != 4) - { - dbg_msg("datafile", "wrong version. version=%x", header.version); - return 0; - } - - /* read in the rest except the data */ - size = 0; - size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE); - size += (header.num_items+header.num_raw_data)*sizeof(int); - if(header.version == 4) - size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */ - size += header.item_size; - - allocsize = size; - allocsize += sizeof(DATAFILE); /* add space for info structure */ - allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */ - - df = (DATAFILE*)mem_alloc(allocsize, 1); - df->header = header; - df->data_start_offset = sizeof(DATAFILE_HEADER) + size; - df->data_ptrs = (char**)(df+1); - df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *); - df->file = file; - - /* clear the data pointers */ - mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*)); - - /* read types, offsets, sizes and item data */ - readsize = io_read(file, df->data, size); - if(readsize != size) - { - dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); - return 0; - } - -#if defined(CONF_ARCH_ENDIAN_BIG) - swap_endian(df->data, sizeof(int), header.swaplen); -#endif - - if(DEBUG) - dbg_msg("datafile", "item_size=%d", df->header.item_size); - - df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data; - df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types]; - df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items]; - df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data]; - - if(header.version == 4) - df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data]; - else - df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data]; - df->info.data_start = df->info.item_start + df->header.item_size; - - if(DEBUG) - dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); - - if(DEBUG) - { - /* - for(int i = 0; i < df->data.num_raw_data; i++) - { - void *p = datafile_get_data(df, i); - dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); - } - - for(int i = 0; i < datafile_num_items(df); i++) - { - int type, id; - void *data = datafile_get_item(df, i, &type, &id); - dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); - int *idata = (int*)data; - for(int k = 0; k < 3; k++) - dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); - } - - for(int i = 0; i < df->data.num_item_types; i++) - { - dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, - df->info.item_types[i].type, - df->info.item_types[i].start, - df->info.item_types[i].num); - for(int k = 0; k < df->info.item_types[i].num; k++) - { - int type, id; - datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); - if(type != df->info.item_types[i].type) - dbg_msg("map", "\tERROR"); - } - } - */ - } - - return df; -} - -int datafile_num_data(DATAFILE *df) -{ - return df->header.num_raw_data; -} - -/* always returns the size in the file */ -int datafile_get_datasize(DATAFILE *df, int index) -{ - if(index == df->header.num_raw_data-1) - return df->header.data_size-df->info.data_offsets[index]; - return df->info.data_offsets[index+1]-df->info.data_offsets[index]; -} - -void *datafile_get_data(DATAFILE *df, int index) -{ - /* load it if needed */ - if(!df->data_ptrs[index]) - { - /* fetch the data size */ - int datasize = datafile_get_datasize(df, index); - - if(df->header.version == 4) - { - /* v4 has compressed data */ - void *temp = (char *)mem_alloc(datasize, 1); - unsigned long uncompressed_size = df->info.data_sizes[index]; - unsigned long s; - - dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); - df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1); - - /* read the compressed data */ - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, temp, datasize); - - /* decompress the data, TODO: check for errors */ - s = uncompressed_size; - uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize); - - /* clean up the temporary buffers */ - mem_free(temp); - } - else - { - /* load the data */ - dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); - df->data_ptrs[index] = (char *)mem_alloc(datasize, 1); - io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); - io_read(df->file, df->data_ptrs[index], datasize); - } - } - - return df->data_ptrs[index]; -} - -void datafile_unload_data(DATAFILE *df, int index) -{ - /* */ - mem_free(df->data_ptrs[index]); - df->data_ptrs[index] = 0x0; -} - -int datafile_get_itemsize(DATAFILE *df, int index) -{ - if(index == df->header.num_items-1) - return df->header.item_size-df->info.item_offsets[index]; - return df->info.item_offsets[index+1]-df->info.item_offsets[index]; -} - -void *datafile_get_item(DATAFILE *df, int index, int *type, int *id) -{ - DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]); - if(type) - *type = (i->type_and_id>>16)&0xffff; /* remove sign extention */ - if(id) - *id = i->type_and_id&0xffff; - return (void *)(i+1); -} - -void datafile_get_type(DATAFILE *df, int type, int *start, int *num) -{ - int i; - for(i = 0; i < df->header.num_item_types; i++) - { - if(df->info.item_types[i].type == type) - { - *start = df->info.item_types[i].start; - *num = df->info.item_types[i].num; - return; - } - } - - *start = 0; - *num = 0; -} - -void *datafile_find_item(DATAFILE *df, int type, int id) -{ - int start, num,i ; - int item_id; - void *item; - - datafile_get_type(df, type, &start, &num); - for(i = 0; i < num; i++) - { - item = datafile_get_item(df, start+i,0, &item_id); - if(id == item_id) - return item; - } - return 0; -} - -int datafile_num_items(DATAFILE *df) -{ - return df->header.num_items; -} - -void datafile_unload(DATAFILE *df) -{ - if(df) - { - /* free the data that is loaded */ - int i; - for(i = 0; i < df->header.num_raw_data; i++) - mem_free(df->data_ptrs[i]); - - io_close(df->file); - mem_free(df); - } -} - -/* DATAFILE output */ -typedef struct -{ - int uncompressed_size; - int compressed_size; - void *compressed_data; -} DATA_INFO; - -typedef struct -{ - int type; - int id; - int size; - int next; - int prev; - void *data; -} ITEM_INFO; - -typedef struct -{ - int num; - int first; - int last; -} ITEMTYPE_INFO; - -/* */ -struct DATAFILE_OUT_t -{ - IOHANDLE file; - int num_items; - int num_datas; - int num_item_types; - ITEMTYPE_INFO item_types[0xffff]; - ITEM_INFO items[1024]; - DATA_INFO datas[1024]; -}; - -DATAFILE_OUT *datafile_create(const char *filename) -{ - int i; - DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); - df->file = io_open(filename, IOFLAG_WRITE); - if(!df->file) - { - mem_free(df); - return 0; - } - - df->num_items = 0; - df->num_datas = 0; - df->num_item_types = 0; - mem_zero(&df->item_types, sizeof(df->item_types)); - - for(i = 0; i < 0xffff; i++) - { - df->item_types[i].first = -1; - df->item_types[i].last = -1; - } - - return df; -} - -int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data) -{ - df->items[df->num_items].type = type; - df->items[df->num_items].id = id; - df->items[df->num_items].size = size; - - /* - dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); - int i; - for(i = 0; i < size/4; i++) - dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); - */ - - /* copy data */ - df->items[df->num_items].data = mem_alloc(size, 1); - mem_copy(df->items[df->num_items].data, data, size); - - if(!df->item_types[type].num) /* count item types */ - df->num_item_types++; - - /* link */ - df->items[df->num_items].prev = df->item_types[type].last; - df->items[df->num_items].next = -1; - - if(df->item_types[type].last != -1) - df->items[df->item_types[type].last].next = df->num_items; - df->item_types[type].last = df->num_items; - - if(df->item_types[type].first == -1) - df->item_types[type].first = df->num_items; - - df->item_types[type].num++; - - df->num_items++; - return df->num_items-1; -} - -int datafile_add_data(DATAFILE_OUT *df, int size, void *data) -{ - DATA_INFO *info = &df->datas[df->num_datas]; - void *compdata = mem_alloc(size, 1); /* temporary buffer that we use duing compression */ - unsigned long s = size; - - info->uncompressed_size = size; - if(compress((Bytef*)compdata, &s, (Bytef*)data, size) != Z_OK) - dbg_assert(0, "zlib error"); - info->compressed_size = (int)s; - info->compressed_data = mem_alloc(info->compressed_size, 1); - mem_copy(info->compressed_data, compdata, info->compressed_size); - mem_free(compdata); - - df->num_datas++; - return df->num_datas-1; -} - -int datafile_finish(DATAFILE_OUT *df) -{ - int itemsize = 0; - int i, count, offset; - int typessize, headersize, offsetsize, filesize, swapsize; - int datasize = 0; - DATAFILE_ITEM_TYPE info; - DATAFILE_ITEM itm; - DATAFILE_HEADER header; - - /* we should now write this file! */ - if(DEBUG) - dbg_msg("datafile", "writing"); - - /* calculate sizes */ - for(i = 0; i < df->num_items; i++) - { - if(DEBUG) - dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM)); - itemsize += df->items[i].size + sizeof(DATAFILE_ITEM); - } - - - for(i = 0; i < df->num_datas; i++) - datasize += df->datas[i].compressed_size; - - /* calculate the complete size */ - typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE); - headersize = sizeof(DATAFILE_HEADER); - offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); - filesize = headersize + typessize + offsetsize + itemsize + datasize; - swapsize = filesize - datasize; - - (void)swapsize; - - if(DEBUG) - dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); - - /* construct header */ - { - header.id[0] = 'D'; - header.id[1] = 'A'; - header.id[2] = 'T'; - header.id[3] = 'A'; - header.version = 4; - header.size = filesize - 16; - header.swaplen = swapsize - 16; - header.num_item_types = df->num_item_types; - header.num_items = df->num_items; - header.num_raw_data = df->num_datas; - header.item_size = itemsize; - header.data_size = datasize; - - /* TODO: apply swapping */ - /* write header */ - if(DEBUG) - dbg_msg("datafile", "headersize=%d", sizeof(header)); - io_write(df->file, &header, sizeof(header)); - } - - /* write types */ - for(i = 0, count = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write info */ - info.type = i; - info.start = count; - info.num = df->item_types[i].num; - if(DEBUG) - dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); - io_write(df->file, &info, sizeof(info)); - count += df->item_types[i].num; - } - } - - /* write item offsets */ - for(i = 0, offset = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - if(DEBUG) - dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->items[k].size + sizeof(DATAFILE_ITEM); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data offsets */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - io_write(df->file, &offset, sizeof(offset)); - offset += df->datas[i].compressed_size; - } - - /* write data uncompressed sizes */ - for(i = 0, offset = 0; i < df->num_datas; i++) - { - /* - if(DEBUG) - dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); - */ - io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int)); - } - - /* write items */ - for(i = 0; i < 0xffff; i++) - { - if(df->item_types[i].num) - { - /* write all items in of this type */ - int k = df->item_types[i].first; - while(k != -1) - { - itm.type_and_id = (i<<16)|df->items[k].id; - itm.size = df->items[k].size; - if(DEBUG) - dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); - - io_write(df->file, &itm, sizeof(itm)); - io_write(df->file, df->items[k].data, df->items[k].size); - - /* next */ - k = df->items[k].next; - } - } - } - - /* write data */ - for(i = 0; i < df->num_datas; i++) - { - if(DEBUG) - dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size); - io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size); - } - - /* free data */ - for(i = 0; i < df->num_items; i++) - mem_free(df->items[i].data); - - - io_close(df->file); - mem_free(df); - - if(DEBUG) - dbg_msg("datafile", "done"); - return 0; -} - -#define BUFFER_SIZE 64*1024 - -int datafile_crc(const char *filename) -{ - unsigned char buffer[BUFFER_SIZE]; - IOHANDLE file; - int crc = 0; - unsigned bytes = 0; - - file = io_open(filename, IOFLAG_READ); - if(!file) - return 0; - - while(1) - { - bytes = io_read(file, buffer, BUFFER_SIZE); - if(bytes <= 0) - break; - crc = crc32(crc, buffer, bytes); - } - - io_close(file); - - return crc; -} diff --git a/src/engine/datafile.h b/src/engine/datafile.h deleted file mode 100644 index 7cc7b209..00000000 --- a/src/engine/datafile.h +++ /dev/null @@ -1,27 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -/* raw datafile access */ -typedef struct DATAFILE_t DATAFILE; - -/* read access */ -DATAFILE *datafile_load(const char *filename); -DATAFILE *datafile_load_old(const char *filename); -void *datafile_get_data(DATAFILE *df, int index); -int datafile_get_datasize(DATAFILE *df, int index); -void datafile_unload_data(DATAFILE *df, int index); -void *datafile_get_item(DATAFILE *df, int index, int *type, int *id); -int datafile_get_itemsize(DATAFILE *df, int index); -void datafile_get_type(DATAFILE *df, int type, int *start, int *num); -void *datafile_find_item(DATAFILE *df, int type, int id); -int datafile_num_items(DATAFILE *df); -int datafile_num_data(DATAFILE *df); -void datafile_unload(DATAFILE *df); - -int datafile_crc(const char *filename); - -/* write access */ -typedef struct DATAFILE_OUT_t DATAFILE_OUT; -DATAFILE_OUT *datafile_create(const char *filename); -int datafile_add_data(DATAFILE_OUT *df, int size, void *data); -int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data); -int datafile_finish(DATAFILE_OUT *df); diff --git a/src/engine/detect.h b/src/engine/detect.h deleted file mode 100644 index 04cca2d9..00000000 --- a/src/engine/detect.h +++ /dev/null @@ -1,115 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef BASELIB_FILE_CONFIG_H -#define BASELIB_FILE_CONFIG_H - -/* - this file detected the family, platform and architecture - to compile for. -*/ - -/* platforms */ - -/* windows Family */ -#if defined(WIN64) || defined(_WIN64) - /* Hmm, is this IA64 or x86-64? */ - #define CONF_FAMILY_WINDOWS 1 - #define CONF_FAMILY_STRING "windows" - #define CONF_PLATFORM_WIN64 1 - #define CONF_PLATFORM_STRING "win64" -#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) - #define CONF_FAMILY_WINDOWS 1 - #define CONF_FAMILY_STRING "windows" - #define CONF_PLATFORM_WIN32 1 - #define CONF_PLATFORM_STRING "win32" -#endif - -/* unix family */ -#if defined(__FreeBSD__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_FREEBSD 1 - #define CONF_PLATFORM_STRING "freebsd" -#endif - -#if defined(__OpenBSD__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_OPENBSD 1 - #define CONF_PLATFORM_STRING "openbsd" -#endif - -#if defined(__LINUX__) || defined(__linux__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_LINUX 1 - #define CONF_PLATFORM_STRING "linux" -#endif - -#if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFORM_MACOSX 1 - #define CONF_PLATFORM_STRING "macosx" -#endif - -#if defined(__sun) - #define CONF_FAMILY_UNIX 1 - #define CONF_FAMILY_STRING "unix" - #define CONF_PLATFROM_SOLARIS 1 - #define CONF_PLATFORM_STRING "solaris" -#endif - -/* beos family */ -#if defined(__BeOS) || defined(__BEOS__) - #define CONF_FAMILY_BEOS 1 - #define CONF_FAMILY_STRING "beos" - #define CONF_PLATFORM_BEOS 1 - #define CONF_PLATFORM_STRING "beos" -#endif - - -/* architectures */ -#if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) - #define CONF_ARCH_IA32 1 - #define CONF_ARCH_STRING "ia32" - #define CONF_ARCH_ENDIAN_LITTLE 1 -#endif - -#if defined(__ia64__) - #define CONF_ARCH_IA64 1 - #define CONF_ARCH_STRING "ia64" - #define CONF_ARCH_ENDIAN_LITTLE 1 -#endif - -#if defined(__amd64__) || defined(__x86_64__) - #define CONF_ARCH_AMD64 1 - #define CONF_ARCH_STRING "amd64" - #define CONF_ARCH_ENDIAN_LITTLE 1 -#endif - -#if defined(__powerpc__) || defined(__ppc__) - #define CONF_ARCH_PPC 1 - #define CONF_ARCH_STRING "ppc" - #define CONF_ARCH_ENDIAN_BIG 1 -#endif - -#if defined(__sparc__) - #define CONF_ARCH_SPARC 1 - #define CONF_ARCH_STRING "sparc" - #define CONF_ARCH_ENDIAN_BIG 1 -#endif - - -#ifndef CONF_FAMILY_STRING -#define CONF_FAMILY_STRING "unknown" -#endif - -#ifndef CONF_PLATFORM_STRING -#define CONF_PLATFORM_STRING "unknown" -#endif - -#ifndef CONF_ARCH_STRING -#define CONF_ARCH_STRING "unknown" -#endif - -#endif diff --git a/src/engine/e_compression.c b/src/engine/e_compression.c new file mode 100644 index 00000000..fa7d7866 --- /dev/null +++ b/src/engine/e_compression.c @@ -0,0 +1,147 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" +#include + +/* Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign */ +unsigned char *vint_pack(unsigned char *dst, int i) +{ + *dst = (i>>25)&0x40; /* set sign bit if i<0 */ + i = i^(i>>31); /* if(i<0) i = ~i */ + + *dst |= i&0x3F; /* pack 6bit into dst */ + i >>= 6; /* discard 6 bits */ + if(i) + { + *dst |= 0x80; /* set extend bit */ + while(1) + { + dst++; + *dst = i&(0x7F); /* pack 7bit */ + i >>= 7; /* discard 7 bits */ + *dst |= (i!=0)<<7; /* set extend bit (may branch) */ + if(!i) + break; + } + } + + dst++; + return dst; +} + +const unsigned char *vint_unpack(const unsigned char *src, int *i) +{ + int sign = (*src>>6)&1; + *i = *src&0x3F; + + do + { + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6); + + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6+7); + + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6+7+7); + + if(!(*src&0x80)) break; + src++; + *i |= (*src&(0x7F))<<(6+7+7+7); + } while(0); + + src++; + *i ^= -sign; /* if(sign) *i = ~(*i) */ + return src; +} + + +long intpack_decompress(const void *src_, int size, void *dst_) +{ + const unsigned char *src = (unsigned char *)src_; + const unsigned char *end = src + size; + int *dst = (int *)dst_; + while(src < end) + { + src = vint_unpack(src, dst); + dst++; + } + return (long)((unsigned char *)dst-(unsigned char *)dst_); +} + +long intpack_compress(const void *src_, int size, void *dst_) +{ + int *src = (int *)src_; + unsigned char *dst = (unsigned char *)dst_; + size /= 4; + while(size) + { + dst = vint_pack(dst, *src); + size--; + src++; + } + return (long)(dst-(unsigned char *)dst_); +} + + +/* */ +long zerobit_compress(const void *src_, int size, void *dst_) +{ + unsigned char *src = (unsigned char *)src_; + unsigned char *dst = (unsigned char *)dst_; + + while(size) + { + unsigned char bit = 0x80; + unsigned char mask = 0; + int dst_move = 1; + int chunk = size < 8 ? size : 8; + int b; + size -= chunk; + + for(b = 0; b < chunk; b++, bit>>=1) + { + if(*src) + { + dst[dst_move] = *src; + mask |= bit; + dst_move++; + } + + src++; + } + + *dst = mask; + dst += dst_move; + } + + return (long)(dst-(unsigned char *)dst_); +} + +long zerobit_decompress(const void *src_, int size, void *dst_) +{ + unsigned char *src = (unsigned char *)src_; + unsigned char *dst = (unsigned char *)dst_; + unsigned char *end = src + size; + + + while(src != end) + { + unsigned char bit = 0x80; + unsigned char mask = *src++; + int b; + + for(b = 0; b < 8; b++, bit>>=1) + { + if(mask&bit) + *dst++ = *src++; + else + *dst++ = 0; + } + } + + return (long)(dst-(unsigned char *)dst_); +} + diff --git a/src/engine/e_compression.h b/src/engine/e_compression.h new file mode 100644 index 00000000..39ffd596 --- /dev/null +++ b/src/engine/e_compression.h @@ -0,0 +1,11 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +/* variable int packing */ +unsigned char *vint_pack(unsigned char *dst, int i); +const unsigned char *vint_unpack(const unsigned char *src, int *inout); +long intpack_compress(const void *src, int size, void *dst); +long intpack_decompress(const void *src, int size, void *dst); + +/* zerobit packing */ +long zerobit_compress(const void *src, int size, void *dst); +long zerobit_decompress(const void *src, int size, void *dst); diff --git a/src/engine/e_config.c b/src/engine/e_config.c new file mode 100644 index 00000000..a4c47ec1 --- /dev/null +++ b/src/engine/e_config.c @@ -0,0 +1,196 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include +#include +#include + +#include "e_system.h" +#include "e_config.h" + + +/* buffered stream for reading lines, should perhaps be something smaller */ +typedef struct +{ + char buffer[4*1024]; + unsigned buffer_pos; + unsigned buffer_size; + unsigned buffer_max_size; + IOHANDLE io; +} LINEREADER; + +void linereader_init(LINEREADER *lr, IOHANDLE io) +{ + lr->buffer_max_size = 4*1024; + lr->buffer_size = 0; + lr->buffer_pos = 0; + lr->io = io; +} + +char *linereader_get(LINEREADER *lr) +{ + unsigned line_start = lr->buffer_pos; + + while(1) + { + if(lr->buffer_pos >= lr->buffer_size) + { + /* fetch more */ + + /* move the remaining part to the front */ + unsigned read; + unsigned left = lr->buffer_size - line_start; + + if(line_start > lr->buffer_size) + left = 0; + if(left) + mem_move(lr->buffer, &lr->buffer[line_start], left); + lr->buffer_pos = left; + + /* fill the buffer */ + read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos); + lr->buffer_size = left + read; + line_start = 0; + + if(!read) + { + if(left) + { + lr->buffer[left] = 0; /* return the last line */ + lr->buffer_pos = left; + lr->buffer_size = left; + return lr->buffer; + } + else + return 0x0; /* we are done! */ + } + } + else + { + if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r') + { + /* line found */ + lr->buffer[lr->buffer_pos] = 0; + lr->buffer_pos++; + return &lr->buffer[line_start]; + } + else + lr->buffer_pos++; + } + } +} + +CONFIGURATION config; + +void config_reset() +{ + #define MACRO_CONFIG_INT(name,def,min,max) config.name = def; + #define MACRO_CONFIG_STR(name,len,def) strncpy(config.name, def, len); + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} + +void strip_spaces(char **p) +{ + char *s = *p; + char *end; + + while (*s == ' ') + ++s; + + end = s + strlen(s); + while (end > s && *(end - 1) == ' ') + *--end = 0; +} + +void config_set(const char *line) +{ + const char *c = strchr(line, '='); + if (c) + { + char var[256]; + char val[256]; + char *var_str = var; + char *val_str = val; + + strcpy(val, c+1); + + mem_copy(var, line, c - line); + var[c - line] = 0; + + + strip_spaces(&var_str); + strip_spaces(&val_str); + + #define MACRO_CONFIG_INT(name,def,min,max) { if (strcmp(#name, var_str) == 0) config_set_ ## name (&config, atoi(val_str)); } + #define MACRO_CONFIG_STR(name,len,def) { if (strcmp(#name, var_str) == 0) { config_set_ ## name (&config, val_str); } } + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR + } +} + +void config_load(const char *filename) +{ + IOHANDLE file; + dbg_msg("config/load", "loading %s", filename); + file = io_open(filename, IOFLAG_READ); + + if(file) + { + char *line; + LINEREADER lr; + linereader_init(&lr, file); + + while ((line = linereader_get(&lr))) + config_set(line); + + io_close(file); + } +} + +void config_save(const char *filename) +{ + IOHANDLE file; + dbg_msg("config/save", "saving config to %s", filename); + + file = io_open(filename, IOFLAG_WRITE); + + if(file) + { +#if defined(CONF_FAMILY_WINDOWS) + const char newline[] = "\r\n"; +#else + const char newline[] = "\n"; +#endif + const int newline_len = sizeof(newline)-1; + + #define MACRO_CONFIG_INT(name,def,min,max) { char str[256]; sprintf(str, "%s=%i%s", #name, config.name, newline); io_write(file, str, strlen(str)); } + #define MACRO_CONFIG_STR(name,len,def) { io_write(file, #name, strlen(#name)); io_write(file, "=", 1); io_write(file, config.name, strlen(config.name)); io_write(file, newline, newline_len); } + + #include "e_config_variables.h" + + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR + + io_close(file); + } + else + dbg_msg("config/save", "couldn't open %s for writing. :(", filename); +} + +#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c) { return c->name; } +#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c) { return c->name; } +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR + +#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val) { if(min != max) { if (val < min) val = min; if (max != 0 && val > max) val = max; } c->name = val; } +#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str) { strncpy(c->name, str, len-1); c->name[sizeof(c->name)-1] = 0; } +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR diff --git a/src/engine/e_config.h b/src/engine/e_config.h new file mode 100644 index 00000000..63933660 --- /dev/null +++ b/src/engine/e_config.h @@ -0,0 +1,41 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef _CONFIG_H +#define _CONFIG_H + +#ifdef __cplusplus +extern "C"{ +#endif + +typedef struct +{ + #define MACRO_CONFIG_INT(name,def,min,max) int name; + #define MACRO_CONFIG_STR(name,len,def) char name[len]; + #include "e_config_variables.h" + #undef MACRO_CONFIG_INT + #undef MACRO_CONFIG_STR +} CONFIGURATION; + +extern CONFIGURATION config; + +void config_set(const char *line); +void config_reset(); +void config_load(const char *filename); +void config_save(const char *filename); + +#define MACRO_CONFIG_INT(name,def,min,max) int config_get_ ## name (CONFIGURATION *c); +#define MACRO_CONFIG_STR(name,len,def) const char *config_get_ ## name (CONFIGURATION *c); +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR + +#define MACRO_CONFIG_INT(name,def,min,max) void config_set_ ## name (CONFIGURATION *c, int val); +#define MACRO_CONFIG_STR(name,len,def) void config_set_ ## name (CONFIGURATION *c, const char *str); +#include "e_config_variables.h" +#undef MACRO_CONFIG_INT +#undef MACRO_CONFIG_STR + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/e_config_variables.h b/src/engine/e_config_variables.h new file mode 100644 index 00000000..51ee7376 --- /dev/null +++ b/src/engine/e_config_variables.h @@ -0,0 +1,56 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "../game/g_variables.h" + + +MACRO_CONFIG_STR(player_name, 32, "nameless tee") +MACRO_CONFIG_STR(clan_name, 32, "") +MACRO_CONFIG_STR(password, 32, "") +MACRO_CONFIG_STR(rcon_password, 32, "") + +MACRO_CONFIG_INT(cl_cpu_throttle, 0, 0, 1) +/*MACRO_CONFIG_STR(cl_connect, 32, "")*/ +MACRO_CONFIG_INT(cl_editor, 0, 0, 1) + +MACRO_CONFIG_STR(b_filter_string, 64, "") + +MACRO_CONFIG_INT(b_filter_full, 0, 0, 1) +MACRO_CONFIG_INT(b_filter_empty, 0, 0, 1) +MACRO_CONFIG_INT(b_filter_pw, 0, 0, 1) +MACRO_CONFIG_INT(b_sort, 0, 0, 256) +MACRO_CONFIG_INT(b_max_requests, 10, 0, 1000) + +MACRO_CONFIG_INT(snd_rate, 48000, 0, 0) +MACRO_CONFIG_INT(snd_enable, 1, 0, 1) +MACRO_CONFIG_INT(snd_volume, 100, 0, 100) + +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) +MACRO_CONFIG_INT(gfx_color_depth, 24, 16, 24) +MACRO_CONFIG_INT(gfx_vsync, 1, 0, 1) +MACRO_CONFIG_INT(gfx_display_all_modes, 0, 0, 1) +MACRO_CONFIG_INT(gfx_texture_compression, 0, 0, 1) +MACRO_CONFIG_INT(gfx_high_detail, 1, 0, 1) +MACRO_CONFIG_INT(gfx_texture_quality, 1, 0, 1) +MACRO_CONFIG_INT(gfx_fsaa_samples, 0, 0, 16) +MACRO_CONFIG_INT(gfx_refresh_rate, 0, 0, 0) + +MACRO_CONFIG_INT(key_screenshot, 267, 32, 512) +MACRO_CONFIG_INT(inp_mousesens, 100, 5, 100000) + +MACRO_CONFIG_STR(masterserver, 128, "master.teewars.com") + +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_external_port, 0, 0, 0) +MACRO_CONFIG_INT(sv_sendheartbeats, 1, 0, 1) +MACRO_CONFIG_STR(sv_map, 128, "dm1") +MACRO_CONFIG_INT(sv_map_reload, 0, 0, 1) +MACRO_CONFIG_INT(sv_max_clients, 8, 1, 12) +MACRO_CONFIG_INT(sv_bandwidth_mode, 0, 0, 2) + +MACRO_CONFIG_INT(debug, 0, 0, 1) +MACRO_CONFIG_INT(dbg_stress, 0, 0, 0) +MACRO_CONFIG_STR(dbg_stress_server, 32, "localhost") + diff --git a/src/engine/e_datafile.c b/src/engine/e_datafile.c new file mode 100644 index 00000000..14138ed9 --- /dev/null +++ b/src/engine/e_datafile.c @@ -0,0 +1,630 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" +#include "e_datafile.h" +#include "external/zlib/zlib.h" + +static const int DEBUG=0; + +typedef struct +{ + int type; + int start; + int num; +} DATAFILE_ITEM_TYPE; + +typedef struct +{ + int type_and_id; + int size; +} DATAFILE_ITEM; + +typedef struct +{ + char id[4]; + int version; + int size; + int swaplen; + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; +} DATAFILE_HEADER; + +typedef struct +{ + int num_item_types; + int num_items; + int num_raw_data; + int item_size; + int data_size; + char start[4]; +} DATAFILE_DATA; + +typedef struct +{ + DATAFILE_ITEM_TYPE *item_types; + int *item_offsets; + int *data_offsets; + int *data_sizes; + + char *item_start; + char *data_start; +} DATAFILE_INFO; + +struct DATAFILE_t +{ + IOHANDLE file; + DATAFILE_INFO info; + DATAFILE_HEADER header; + int data_start_offset; + char **data_ptrs; + char *data; +}; + +DATAFILE *datafile_load(const char *filename) +{ + DATAFILE *df; + IOHANDLE file; + DATAFILE_HEADER header; + unsigned readsize; + + unsigned *dst; + unsigned char *src; + unsigned j; + int size = 0; + int allocsize = 0; + + (void)dst; + (void)src; + (void)j; + + + dbg_msg("datafile", "datafile loading. filename='%s'", filename); + + file = io_open(filename, IOFLAG_READ); + if(!file) + return 0; + + /* TODO: change this header */ + io_read(file, &header, sizeof(header)); + if(header.id[0] != 'A' || header.id[1] != 'T' || header.id[2] != 'A' || header.id[3] != 'D') + { + if(header.id[0] != 'D' || header.id[1] != 'A' || header.id[2] != 'T' || header.id[3] != 'A') + { + dbg_msg("datafile", "wrong signature. %x %x %x %x", header.id[0], header.id[1], header.id[2], header.id[3]); + return 0; + } + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(&header, sizeof(int), sizeof(header)/sizeof(int)); +#endif + if(header.version != 3 && header.version != 4) + { + dbg_msg("datafile", "wrong version. version=%x", header.version); + return 0; + } + + /* read in the rest except the data */ + size = 0; + size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE); + size += (header.num_items+header.num_raw_data)*sizeof(int); + if(header.version == 4) + size += header.num_raw_data*sizeof(int); /* v4 has uncompressed data sizes aswell */ + size += header.item_size; + + allocsize = size; + allocsize += sizeof(DATAFILE); /* add space for info structure */ + allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */ + + df = (DATAFILE*)mem_alloc(allocsize, 1); + df->header = header; + df->data_start_offset = sizeof(DATAFILE_HEADER) + size; + df->data_ptrs = (char**)(df+1); + df->data = (char *)(df+1)+header.num_raw_data*sizeof(char *); + df->file = file; + + /* clear the data pointers */ + mem_zero(df->data_ptrs, header.num_raw_data*sizeof(void*)); + + /* read types, offsets, sizes and item data */ + readsize = io_read(file, df->data, size); + if(readsize != size) + { + dbg_msg("datafile", "couldn't load the whole thing, wanted=%d got=%d", size, readsize); + return 0; + } + +#if defined(CONF_ARCH_ENDIAN_BIG) + swap_endian(df->data, sizeof(int), header.swaplen); +#endif + + if(DEBUG) + dbg_msg("datafile", "item_size=%d", df->header.item_size); + + df->info.item_types = (DATAFILE_ITEM_TYPE *)df->data; + df->info.item_offsets = (int *)&df->info.item_types[df->header.num_item_types]; + df->info.data_offsets = (int *)&df->info.item_offsets[df->header.num_items]; + df->info.data_sizes = (int *)&df->info.data_offsets[df->header.num_raw_data]; + + if(header.version == 4) + df->info.item_start = (char *)&df->info.data_sizes[df->header.num_raw_data]; + else + df->info.item_start = (char *)&df->info.data_offsets[df->header.num_raw_data]; + df->info.data_start = df->info.item_start + df->header.item_size; + + if(DEBUG) + dbg_msg("datafile", "datafile loading done. datafile='%s'", filename); + + if(DEBUG) + { + /* + for(int i = 0; i < df->data.num_raw_data; i++) + { + void *p = datafile_get_data(df, i); + dbg_msg("datafile", "%d %d", (int)((char*)p - (char*)(&df->data)), size); + } + + for(int i = 0; i < datafile_num_items(df); i++) + { + int type, id; + void *data = datafile_get_item(df, i, &type, &id); + dbg_msg("map", "\t%d: type=%x id=%x p=%p offset=%d", i, type, id, data, df->info.item_offsets[i]); + int *idata = (int*)data; + for(int k = 0; k < 3; k++) + dbg_msg("datafile", "\t\t%d=%d (%x)", k, idata[k], idata[k]); + } + + for(int i = 0; i < df->data.num_item_types; i++) + { + dbg_msg("map", "\t%d: type=%x start=%d num=%d", i, + df->info.item_types[i].type, + df->info.item_types[i].start, + df->info.item_types[i].num); + for(int k = 0; k < df->info.item_types[i].num; k++) + { + int type, id; + datafile_get_item(df, df->info.item_types[i].start+k, &type, &id); + if(type != df->info.item_types[i].type) + dbg_msg("map", "\tERROR"); + } + } + */ + } + + return df; +} + +int datafile_num_data(DATAFILE *df) +{ + return df->header.num_raw_data; +} + +/* always returns the size in the file */ +int datafile_get_datasize(DATAFILE *df, int index) +{ + if(index == df->header.num_raw_data-1) + return df->header.data_size-df->info.data_offsets[index]; + return df->info.data_offsets[index+1]-df->info.data_offsets[index]; +} + +void *datafile_get_data(DATAFILE *df, int index) +{ + /* load it if needed */ + if(!df->data_ptrs[index]) + { + /* fetch the data size */ + int datasize = datafile_get_datasize(df, index); + + if(df->header.version == 4) + { + /* v4 has compressed data */ + void *temp = (char *)mem_alloc(datasize, 1); + unsigned long uncompressed_size = df->info.data_sizes[index]; + unsigned long s; + + dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); + df->data_ptrs[index] = (char *)mem_alloc(uncompressed_size, 1); + + /* read the compressed data */ + io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); + io_read(df->file, temp, datasize); + + /* decompress the data, TODO: check for errors */ + s = uncompressed_size; + uncompress((Bytef*)df->data_ptrs[index], &s, (Bytef*)temp, datasize); + + /* clean up the temporary buffers */ + mem_free(temp); + } + else + { + /* load the data */ + dbg_msg("datafile", "loading data index=%d size=%d", index, datasize); + df->data_ptrs[index] = (char *)mem_alloc(datasize, 1); + io_seek(df->file, df->data_start_offset+df->info.data_offsets[index], IOSEEK_START); + io_read(df->file, df->data_ptrs[index], datasize); + } + } + + return df->data_ptrs[index]; +} + +void datafile_unload_data(DATAFILE *df, int index) +{ + /* */ + mem_free(df->data_ptrs[index]); + df->data_ptrs[index] = 0x0; +} + +int datafile_get_itemsize(DATAFILE *df, int index) +{ + if(index == df->header.num_items-1) + return df->header.item_size-df->info.item_offsets[index]; + return df->info.item_offsets[index+1]-df->info.item_offsets[index]; +} + +void *datafile_get_item(DATAFILE *df, int index, int *type, int *id) +{ + DATAFILE_ITEM *i = (DATAFILE_ITEM *)(df->info.item_start+df->info.item_offsets[index]); + if(type) + *type = (i->type_and_id>>16)&0xffff; /* remove sign extention */ + if(id) + *id = i->type_and_id&0xffff; + return (void *)(i+1); +} + +void datafile_get_type(DATAFILE *df, int type, int *start, int *num) +{ + int i; + for(i = 0; i < df->header.num_item_types; i++) + { + if(df->info.item_types[i].type == type) + { + *start = df->info.item_types[i].start; + *num = df->info.item_types[i].num; + return; + } + } + + *start = 0; + *num = 0; +} + +void *datafile_find_item(DATAFILE *df, int type, int id) +{ + int start, num,i ; + int item_id; + void *item; + + datafile_get_type(df, type, &start, &num); + for(i = 0; i < num; i++) + { + item = datafile_get_item(df, start+i,0, &item_id); + if(id == item_id) + return item; + } + return 0; +} + +int datafile_num_items(DATAFILE *df) +{ + return df->header.num_items; +} + +void datafile_unload(DATAFILE *df) +{ + if(df) + { + /* free the data that is loaded */ + int i; + for(i = 0; i < df->header.num_raw_data; i++) + mem_free(df->data_ptrs[i]); + + io_close(df->file); + mem_free(df); + } +} + +/* DATAFILE output */ +typedef struct +{ + int uncompressed_size; + int compressed_size; + void *compressed_data; +} DATA_INFO; + +typedef struct +{ + int type; + int id; + int size; + int next; + int prev; + void *data; +} ITEM_INFO; + +typedef struct +{ + int num; + int first; + int last; +} ITEMTYPE_INFO; + +/* */ +struct DATAFILE_OUT_t +{ + IOHANDLE file; + int num_items; + int num_datas; + int num_item_types; + ITEMTYPE_INFO item_types[0xffff]; + ITEM_INFO items[1024]; + DATA_INFO datas[1024]; +}; + +DATAFILE_OUT *datafile_create(const char *filename) +{ + int i; + DATAFILE_OUT *df = (DATAFILE_OUT*)mem_alloc(sizeof(DATAFILE_OUT), 1); + df->file = io_open(filename, IOFLAG_WRITE); + if(!df->file) + { + mem_free(df); + return 0; + } + + df->num_items = 0; + df->num_datas = 0; + df->num_item_types = 0; + mem_zero(&df->item_types, sizeof(df->item_types)); + + for(i = 0; i < 0xffff; i++) + { + df->item_types[i].first = -1; + df->item_types[i].last = -1; + } + + return df; +} + +int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data) +{ + df->items[df->num_items].type = type; + df->items[df->num_items].id = id; + df->items[df->num_items].size = size; + + /* + dbg_msg("datafile", "added item type=%d id=%d size=%d", type, id, size); + int i; + for(i = 0; i < size/4; i++) + dbg_msg("datafile", "\t%d: %08x %d", i, ((int*)data)[i], ((int*)data)[i]); + */ + + /* copy data */ + df->items[df->num_items].data = mem_alloc(size, 1); + mem_copy(df->items[df->num_items].data, data, size); + + if(!df->item_types[type].num) /* count item types */ + df->num_item_types++; + + /* link */ + df->items[df->num_items].prev = df->item_types[type].last; + df->items[df->num_items].next = -1; + + if(df->item_types[type].last != -1) + df->items[df->item_types[type].last].next = df->num_items; + df->item_types[type].last = df->num_items; + + if(df->item_types[type].first == -1) + df->item_types[type].first = df->num_items; + + df->item_types[type].num++; + + df->num_items++; + return df->num_items-1; +} + +int datafile_add_data(DATAFILE_OUT *df, int size, void *data) +{ + DATA_INFO *info = &df->datas[df->num_datas]; + void *compdata = mem_alloc(size, 1); /* temporary buffer that we use duing compression */ + unsigned long s = size; + + info->uncompressed_size = size; + if(compress((Bytef*)compdata, &s, (Bytef*)data, size) != Z_OK) + dbg_assert(0, "zlib error"); + info->compressed_size = (int)s; + info->compressed_data = mem_alloc(info->compressed_size, 1); + mem_copy(info->compressed_data, compdata, info->compressed_size); + mem_free(compdata); + + df->num_datas++; + return df->num_datas-1; +} + +int datafile_finish(DATAFILE_OUT *df) +{ + int itemsize = 0; + int i, count, offset; + int typessize, headersize, offsetsize, filesize, swapsize; + int datasize = 0; + DATAFILE_ITEM_TYPE info; + DATAFILE_ITEM itm; + DATAFILE_HEADER header; + + /* we should now write this file! */ + if(DEBUG) + dbg_msg("datafile", "writing"); + + /* calculate sizes */ + for(i = 0; i < df->num_items; i++) + { + if(DEBUG) + dbg_msg("datafile", "item=%d size=%d (%d)", i, df->items[i].size, df->items[i].size+sizeof(DATAFILE_ITEM)); + itemsize += df->items[i].size + sizeof(DATAFILE_ITEM); + } + + + for(i = 0; i < df->num_datas; i++) + datasize += df->datas[i].compressed_size; + + /* calculate the complete size */ + typessize = df->num_item_types*sizeof(DATAFILE_ITEM_TYPE); + headersize = sizeof(DATAFILE_HEADER); + offsetsize = df->num_items*sizeof(int) + df->num_datas*sizeof(int); + filesize = headersize + typessize + offsetsize + itemsize + datasize; + swapsize = filesize - datasize; + + (void)swapsize; + + if(DEBUG) + dbg_msg("datafile", "num_item_types=%d typessize=%d itemsize=%d datasize=%d", df->num_item_types, typessize, itemsize, datasize); + + /* construct header */ + { + header.id[0] = 'D'; + header.id[1] = 'A'; + header.id[2] = 'T'; + header.id[3] = 'A'; + header.version = 4; + header.size = filesize - 16; + header.swaplen = swapsize - 16; + header.num_item_types = df->num_item_types; + header.num_items = df->num_items; + header.num_raw_data = df->num_datas; + header.item_size = itemsize; + header.data_size = datasize; + + /* TODO: apply swapping */ + /* write header */ + if(DEBUG) + dbg_msg("datafile", "headersize=%d", sizeof(header)); + io_write(df->file, &header, sizeof(header)); + } + + /* write types */ + for(i = 0, count = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write info */ + info.type = i; + info.start = count; + info.num = df->item_types[i].num; + if(DEBUG) + dbg_msg("datafile", "writing type=%x start=%d num=%d", info.type, info.start, info.num); + io_write(df->file, &info, sizeof(info)); + count += df->item_types[i].num; + } + } + + /* write item offsets */ + for(i = 0, offset = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write all items in of this type */ + int k = df->item_types[i].first; + while(k != -1) + { + if(DEBUG) + dbg_msg("datafile", "writing item offset num=%d offset=%d", k, offset); + io_write(df->file, &offset, sizeof(offset)); + offset += df->items[k].size + sizeof(DATAFILE_ITEM); + + /* next */ + k = df->items[k].next; + } + } + } + + /* write data offsets */ + for(i = 0, offset = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + io_write(df->file, &offset, sizeof(offset)); + offset += df->datas[i].compressed_size; + } + + /* write data uncompressed sizes */ + for(i = 0, offset = 0; i < df->num_datas; i++) + { + /* + if(DEBUG) + dbg_msg("datafile", "writing data offset num=%d offset=%d", i, offset); + */ + io_write(df->file, &df->datas[i].uncompressed_size, sizeof(int)); + } + + /* write items */ + for(i = 0; i < 0xffff; i++) + { + if(df->item_types[i].num) + { + /* write all items in of this type */ + int k = df->item_types[i].first; + while(k != -1) + { + itm.type_and_id = (i<<16)|df->items[k].id; + itm.size = df->items[k].size; + if(DEBUG) + dbg_msg("datafile", "writing item type=%x idx=%d id=%d size=%d", i, k, df->items[k].id, df->items[k].size); + + io_write(df->file, &itm, sizeof(itm)); + io_write(df->file, df->items[k].data, df->items[k].size); + + /* next */ + k = df->items[k].next; + } + } + } + + /* write data */ + for(i = 0; i < df->num_datas; i++) + { + if(DEBUG) + dbg_msg("datafile", "writing data id=%d size=%d", i, df->datas[i].compressed_size); + io_write(df->file, df->datas[i].compressed_data, df->datas[i].compressed_size); + } + + /* free data */ + for(i = 0; i < df->num_items; i++) + mem_free(df->items[i].data); + + + io_close(df->file); + mem_free(df); + + if(DEBUG) + dbg_msg("datafile", "done"); + return 0; +} + +#define BUFFER_SIZE 64*1024 + +int datafile_crc(const char *filename) +{ + unsigned char buffer[BUFFER_SIZE]; + IOHANDLE file; + int crc = 0; + unsigned bytes = 0; + + file = io_open(filename, IOFLAG_READ); + if(!file) + return 0; + + while(1) + { + bytes = io_read(file, buffer, BUFFER_SIZE); + if(bytes <= 0) + break; + crc = crc32(crc, buffer, bytes); + } + + io_close(file); + + return crc; +} diff --git a/src/engine/e_datafile.h b/src/engine/e_datafile.h new file mode 100644 index 00000000..7cc7b209 --- /dev/null +++ b/src/engine/e_datafile.h @@ -0,0 +1,27 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +/* raw datafile access */ +typedef struct DATAFILE_t DATAFILE; + +/* read access */ +DATAFILE *datafile_load(const char *filename); +DATAFILE *datafile_load_old(const char *filename); +void *datafile_get_data(DATAFILE *df, int index); +int datafile_get_datasize(DATAFILE *df, int index); +void datafile_unload_data(DATAFILE *df, int index); +void *datafile_get_item(DATAFILE *df, int index, int *type, int *id); +int datafile_get_itemsize(DATAFILE *df, int index); +void datafile_get_type(DATAFILE *df, int type, int *start, int *num); +void *datafile_find_item(DATAFILE *df, int type, int id); +int datafile_num_items(DATAFILE *df); +int datafile_num_data(DATAFILE *df); +void datafile_unload(DATAFILE *df); + +int datafile_crc(const char *filename); + +/* write access */ +typedef struct DATAFILE_OUT_t DATAFILE_OUT; +DATAFILE_OUT *datafile_create(const char *filename); +int datafile_add_data(DATAFILE_OUT *df, int size, void *data); +int datafile_add_item(DATAFILE_OUT *df, int type, int id, int size, void *data); +int datafile_finish(DATAFILE_OUT *df); diff --git a/src/engine/e_detect.h b/src/engine/e_detect.h new file mode 100644 index 00000000..04cca2d9 --- /dev/null +++ b/src/engine/e_detect.h @@ -0,0 +1,115 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASELIB_FILE_CONFIG_H +#define BASELIB_FILE_CONFIG_H + +/* + this file detected the family, platform and architecture + to compile for. +*/ + +/* platforms */ + +/* windows Family */ +#if defined(WIN64) || defined(_WIN64) + /* Hmm, is this IA64 or x86-64? */ + #define CONF_FAMILY_WINDOWS 1 + #define CONF_FAMILY_STRING "windows" + #define CONF_PLATFORM_WIN64 1 + #define CONF_PLATFORM_STRING "win64" +#elif defined(WIN32) || defined(_WIN32) || defined(__CYGWIN32__) || defined(__MINGW32__) + #define CONF_FAMILY_WINDOWS 1 + #define CONF_FAMILY_STRING "windows" + #define CONF_PLATFORM_WIN32 1 + #define CONF_PLATFORM_STRING "win32" +#endif + +/* unix family */ +#if defined(__FreeBSD__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_FREEBSD 1 + #define CONF_PLATFORM_STRING "freebsd" +#endif + +#if defined(__OpenBSD__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_OPENBSD 1 + #define CONF_PLATFORM_STRING "openbsd" +#endif + +#if defined(__LINUX__) || defined(__linux__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_LINUX 1 + #define CONF_PLATFORM_STRING "linux" +#endif + +#if defined(MACOSX) || defined(__APPLE__) || defined(__DARWIN__) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFORM_MACOSX 1 + #define CONF_PLATFORM_STRING "macosx" +#endif + +#if defined(__sun) + #define CONF_FAMILY_UNIX 1 + #define CONF_FAMILY_STRING "unix" + #define CONF_PLATFROM_SOLARIS 1 + #define CONF_PLATFORM_STRING "solaris" +#endif + +/* beos family */ +#if defined(__BeOS) || defined(__BEOS__) + #define CONF_FAMILY_BEOS 1 + #define CONF_FAMILY_STRING "beos" + #define CONF_PLATFORM_BEOS 1 + #define CONF_PLATFORM_STRING "beos" +#endif + + +/* architectures */ +#if defined(i386) || defined(__i386__) || defined(__x86__) || defined(CONF_PLATFORM_WIN32) + #define CONF_ARCH_IA32 1 + #define CONF_ARCH_STRING "ia32" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__ia64__) + #define CONF_ARCH_IA64 1 + #define CONF_ARCH_STRING "ia64" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__amd64__) || defined(__x86_64__) + #define CONF_ARCH_AMD64 1 + #define CONF_ARCH_STRING "amd64" + #define CONF_ARCH_ENDIAN_LITTLE 1 +#endif + +#if defined(__powerpc__) || defined(__ppc__) + #define CONF_ARCH_PPC 1 + #define CONF_ARCH_STRING "ppc" + #define CONF_ARCH_ENDIAN_BIG 1 +#endif + +#if defined(__sparc__) + #define CONF_ARCH_SPARC 1 + #define CONF_ARCH_STRING "sparc" + #define CONF_ARCH_ENDIAN_BIG 1 +#endif + + +#ifndef CONF_FAMILY_STRING +#define CONF_FAMILY_STRING "unknown" +#endif + +#ifndef CONF_PLATFORM_STRING +#define CONF_PLATFORM_STRING "unknown" +#endif + +#ifndef CONF_ARCH_STRING +#define CONF_ARCH_STRING "unknown" +#endif + +#endif diff --git a/src/engine/e_engine.c b/src/engine/e_engine.c new file mode 100644 index 00000000..341bf936 --- /dev/null +++ b/src/engine/e_engine.c @@ -0,0 +1,66 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include +#include +#include + +static char application_save_path[512] = {0}; + +const char *engine_savepath(const char *filename, char *buffer, int max) +{ + sprintf(buffer, "%s/%s", application_save_path, filename); + return buffer; +} + +void engine_init(const char *appname, int argc, char **argv) +{ + /* init the network */ + net_init(); + + /* create storage location */ + { + char path[1024] = {0}; + fs_storage_path(appname, application_save_path, sizeof(application_save_path)); + if(fs_makedir(application_save_path) == 0) + { + strcpy(path, application_save_path); + strcat(path, "/screenshots"); + fs_makedir(path); + } + } + + /* reset the config */ + config_reset(); + + /* load the configuration */ + { + int i; + const char *config_filename = "default.cfg"; + char buf[1024]; + for(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(engine_savepath(config_filename, buf, sizeof(buf))); + } + + /* search arguments for overrides */ + { + int i; + for(i = 1; i < argc; i++) + config_set(argv[i]); + } +} + +void engine_writeconfig() +{ + char buf[1024]; + config_save(engine_savepath("default.cfg", buf, sizeof(buf))); +} diff --git a/src/engine/e_engine.h b/src/engine/e_engine.h new file mode 100644 index 00000000..56fec20e --- /dev/null +++ b/src/engine/e_engine.h @@ -0,0 +1,5 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +const char *engine_savepath(const char *filename, char *buffer, int max); +void engine_init(const char *appname, int argc, char **argv); +void engine_writeconfig(); diff --git a/src/engine/e_interface.h b/src/engine/e_interface.h new file mode 100644 index 00000000..67f8c78c --- /dev/null +++ b/src/engine/e_interface.h @@ -0,0 +1,858 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_INTERFACE_H +#define ENGINE_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Title: Engine Interface +*/ + +#include "e_keys.h" + +enum +{ + MAX_CLIENTS=12, + SERVER_TICK_SPEED=50, /* TODO: this should be removed */ + SNAP_CURRENT=0, + SNAP_PREV=1, + + IMG_RGB=0, + IMG_RGBA=1, + + MASK_NONE=0, + MASK_SET, + MASK_ZERO, + + SNDFLAG_LOOP=1, + SNDFLAG_POS=2, + SNDFLAG_ALL=3, + + CLIENTSTATE_OFFLINE=0, + CLIENTSTATE_CONNECTING, + CLIENTSTATE_LOADING, + CLIENTSTATE_ONLINE, + CLIENTSTATE_QUITING, + + BROWSESORT_NAME = 0, + BROWSESORT_PING, + BROWSESORT_MAP, + BROWSESORT_GAMETYPE, + BROWSESORT_PROGRESSION, + BROWSESORT_NUMPLAYERS +}; + +typedef struct +{ + int type; + int id; +} SNAP_ITEM; + +typedef struct +{ + const char *name; + int latency; +} CLIENT_INFO; + +typedef struct +{ + int width, height; + int format; + void *data; +} IMAGE_INFO; + +typedef struct +{ + int width, height; + int red, green, blue; +} VIDEO_MODE; + +typedef struct +{ + int sorted_index; + int server_index; + + int progression; + int game_type; + int max_players; + int num_players; + int flags; + int latency; /* in ms */ + char name[64]; + char map[32]; + char version[32]; + char address[24]; + char player_names[16][48]; + int player_scores[16]; +} SERVER_INFO; + +struct rect +{ + float x, y, w, h; +}; + +struct rect *ui_screen(); +typedef void (*rect_fun)(const struct rect *r); +void ui_foreach_rect(rect_fun fun); +void ui_scale(float scale); +void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); +void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); +void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right); +void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right); +void ui_margin(const struct rect *original, int pixels, struct rect *new_rect); + +/* image loaders */ +int gfx_load_png(IMAGE_INFO *img, const char *filename); + +/* + Group: Graphics +*/ + +int gfx_init(); +void gfx_shutdown(); +void gfx_swap(); + +float gfx_screenaspect(); + +int gfx_get_video_modes(VIDEO_MODE *list, int maxcount); +void gfx_set_vsync(int val); + +int gfx_window_active(); +int gfx_window_open(); + +/* textures */ +/* + Function: gfx_load_texture + Loads a texture from a file. TGA and PNG supported. + + Arguments: + filename - Null terminated string to the file to load. + + Returns: + An ID to the texture. -1 on failure. + + See Also: + +*/ +int gfx_load_texture(const char *filename); + +/* + Function: gfx_load_texture_raw + Loads a texture from memory. + + Arguments: + w - Width of the texture. + h - Height of the texture. + data - Pointer to the pixel data. + + Returns: + An ID to the texture. -1 on failure. + + Remarks: + The pixel data should be in RGBA format with 8 bit per component. + So the total size of the data should be w*h*4. + + See Also: + +*/ +int gfx_load_texture_raw(int w, int h, int format, const void *data); +/*int gfx_load_mip_texture_raw(int w, int h, int format, const void *data);*/ + +/* + Function: gfx_texture_set + Sets the active texture. + + Arguments: + id - ID to the texture to set. +*/ +void gfx_texture_set(int id); + +/* + Function: gfx_unload_texture + Unloads a texture. + + Arguments: + id - ID to the texture to unload. + + See Also: + , + + Remarks: + NOT IMPLEMENTED +*/ +int gfx_unload_texture(int id); + +void gfx_clear(float r, float g, float b); + +/* + Function: gfx_screenwidth + Returns the screen width. + + See Also: + +*/ +int gfx_screenwidth(); + +/* + Function: gfx_screenheight + Returns the screen height. + + See Also: + +*/ +int gfx_screenheight(); + +/* + Function: gfx_mapscreen + Specifies the coordinate system for the screen. + + Arguments: + tl_x - Top-left X + tl_y - Top-left Y + br_x - Bottom-right X + br_y - Bottom-right y +*/ +void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y); + +/* + Function: gfx_blend_normal + Set the active blending mode to normal (src, 1-src). + + Remarks: + This must be used before calling . + This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). + + See Also: + +*/ +void gfx_blend_normal(); + +/* + Function: gfx_blend_additive + Set the active blending mode to additive (src, one). + + Remarks: + This must be used before calling . + This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE). + + See Also: + +*/ +void gfx_blend_additive(); + +/* + Function: gfx_quads_begin + Begins a quad drawing session. + + Remarks: + This functions resets the rotation, color and subset. + End the session by using . + You can't change texture or blending mode during a session. + + See Also: + +*/ +void gfx_quads_begin(); + +/* + Function: gfx_quads_end + Ends a quad session. + + See Also: + +*/ +void gfx_quads_end(); + +/* + Function: gfx_quads_setrotation + Sets the rotation to use when drawing a quad. + + Arguments: + angle - Angle in radians. + + Remarks: + The angle is reset when is called. +*/ +void gfx_quads_setrotation(float angle); + +/* + Function: gfx_quads_setcolorvertex + Sets the color of a vertex. + + Arguments: + i - Index to the vertex. + r - Red value. + g - Green value. + b - Blue value. + a - Alpha value. + + Remarks: + The color values are from 0.0 to 1.0. + The color is reset when is called. +*/ +void gfx_setcolorvertex(int i, float r, float g, float b, float a); + +/* + Function: gfx_quads_setcolor + Sets the color of all the vertices. + + Arguments: + r - Red value. + g - Green value. + b - Blue value. + a - Alpha value. + + Remarks: + The color values are from 0.0 to 1.0. + The color is reset when is called. +*/ +void gfx_setcolor(float r, float g, float b, float a); + +/* + Function: gfx_quads_setsubset + Sets the uv coordinates to use. + + Arguments: + tl_u - Top-left U value. + tl_v - Top-left V value. + br_u - Bottom-right U value. + br_v - Bottom-right V value. + + Remarks: + O,0 is top-left of the texture and 1,1 is bottom-right. + The color is reset when is called. +*/ +void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v); + +/* + Function: gfx_quads_drawTL + Draws a quad by specifying the top-left point. + + Arguments: + x - X coordinate of the top-left corner. + y - Y coordinate of the top-left corner. + width - Width of the quad. + height - Height of the quad. + + Remarks: + Rotation still occurs from the center of the quad. + You must call before calling this function. + + See Also: + +*/ +void gfx_quads_drawTL(float x, float y, float width, float height); + +/* + Function: gfx_quads_draw + Draws a quad by specifying the center point. + + Arguments: + x - X coordinate of the center. + y - Y coordinate of the center. + width - Width of the quad. + height - Height of the quad. + + Remarks: + You must call before calling this function. + + See Also: + +*/ +void gfx_quads_draw(float x, float y, float w, float h); + +void gfx_quads_draw_freeform( + float x0, float y0, + float x1, float y1, + float x2, float y2, + float x3, float y3); + +void gfx_quads_text(float x, float y, float size, const char *text); + +/* sound (client) */ +int snd_init(); + +void snd_set_channel(int cid, float vol, float pan); + +int snd_load_wv(const char *filename); + +int snd_play_at(int cid, int sid, int flags, float x, float y); +int snd_play(int cid, int sid, int flags); + +void snd_stop(int id); +void snd_set_listener_pos(float x, float y); + +int snd_shutdown(); + +/* + Group: Input +*/ + +/* + Function: inp_mouse_relative + Fetches the mouse movements. + + Arguments: + x - Pointer to the variable that should get the X movement. + y - Pointer to the variable that should get the Y movement. +*/ +void inp_mouse_relative(int *x, int *y); + +int inp_mouse_scroll(); + +/* + Function: inp_key_pressed + Checks if a key is pressed. + + Arguments: + key - Index to the key to check + + Returns: + Returns 1 if the button is pressed, otherwise 0. + + Remarks: + Check keys.h for the keys. +*/ +int inp_key_pressed(int key); + +/* + Group: Map +*/ + +int map_load(const char *mapname); +void map_unload(); + +/* + Function: map_is_loaded + Checks if a map is loaded. + + Returns: + Returns 1 if the button is pressed, otherwise 0. +*/ +int map_is_loaded(); + +/* + Function: map_num_items + Checks the number of items in the loaded map. + + Returns: + Returns the number of items. 0 if no map is loaded. +*/ +int map_num_items(); + +/* + Function: map_find_item + Searches the map for an item. + + Arguments: + type - Item type. + id - Item ID. + + Returns: + Returns a pointer to the item if it exists, otherwise it returns NULL. +*/ +void *map_find_item(int type, int id); + +/* + Function: map_get_item + Gets an item from the loaded map from index. + + Arguments: + index - Item index. + type - Pointer that recives the item type (can be NULL). + id - Pointer that recives the item id (can be NULL). + + Returns: + Returns a pointer to the item if it exists, otherwise it returns NULL. +*/ +void *map_get_item(int index, int *type, int *id); + +/* + Function: map_get_type + Gets the index range of an item type. + + Arguments: + type - Item type to search for. + start - Pointer that recives the starting index. + num - Pointer that recives the number of items. + + Returns: + If the item type is not in the map, start and num will be set to 0. +*/ +void map_get_type(int type, int *start, int *num); + +/* + Function: map_get_data + Fetches a pointer to a raw data chunk in the map. + + Arguments: + index - Index to the data to fetch. + + Returns: + A pointer to the raw data, otherwise 0. +*/ +void *map_get_data(int index); + +/* + Group: Network (Server) +*/ +/* + Function: snap_new_item + Creates a new item that should be sent. + + Arguments: + type - Type of the item. + id - ID of the item. + size - Size of the item. + + Returns: + A pointer to the item data, otherwise 0. + + Remarks: + The item data should only consist pf 4 byte integers as + they are subject to byte swapping. This means that the size + argument should be dividable by 4. +*/ +void *snap_new_item(int type, int id, int size); + +/* + Group: Network (Client) +*/ +/* + Function: snap_num_items + Check the number of items in a snapshot. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + + Returns: + The number of items in the snapshot. +*/ +int snap_num_items(int snapid); + +/* + Function: snap_get_item + Gets an item from a snapshot. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + index - Index of the item. + item - Pointer that recives the item info. + + Returns: + Returns a pointer to the item if it exists, otherwise NULL. +*/ +const void *snap_get_item(int snapid, int index, SNAP_ITEM *item); + +/* + Function: snap_find_item + Searches a snapshot for an item. + + Arguments: + snapid - Snapshot ID to the data to fetch. + * SNAP_PREV for previous snapshot. + * SNAP_CUR for current snapshot. + type - Type of the item. + id - ID of the item. + + Returns: + Returns a pointer to the item if it exists, otherwise NULL. +*/ +const void *snap_find_item(int snapid, int type, int id); + +/* + Function: snap_input + Sets the input data to send to the server. + + Arguments: + data - Pointer to the data. + size - Size of the data. + + Remarks: + The data should only consist of 4 bytes integer as they are + subject to byte swapping. +*/ +void snap_input(void *data, int size); + +/* + Group: Server Callbacks +*/ +/* + Function: mods_init + Called when the server is started. + + Remarks: + It's called after the map is loaded so all map items are available. +*/ +void mods_init(); + +/* + Function: mods_shutdown + Called when the server quits. + + Remarks: + Should be used to clean up all resources used. +*/ +void mods_shutdown(); + +/* + Function: mods_client_enter + Called when a client has joined the game. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + It's called when the client is finished loading and should enter gameplay. +*/ +void mods_client_enter(int cid); + +/* + Function: mods_client_drop + Called when a client drops from the server. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS +*/ +void mods_client_drop(int cid); + +/* + Function: mods_client_input + Called when the server recives new input from a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + input - Pointer to the input data. + size - Size of the data. (NOT IMPLEMENTED YET) +*/ +void mods_client_input(int cid, void *input); + +/* + Function: mods_tick + Called with a regular interval to progress the gameplay. + + Remarks: + The SERVER_TICK_SPEED tells the number of ticks per second. +*/ +void mods_tick(); + +/* + Function: mods_presnap + Called before the server starts to construct snapshots for the clients. +*/ +void mods_presnap(); + +/* + Function: mods_snap + Called to create the snapshot for a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + The game should make a series of calls to to construct + the snapshot for the client. +*/ +void mods_snap(int cid); + +/* + Function: mods_postsnap + Called after the server is done sending the snapshots. +*/ +void mods_postsnap(); + +/* + Group: Client Callbacks +*/ +/* + Function: modc_init + Called when the client starts. + + Remarks: + The game should load resources that are used during the entire + time of the game. No map is loaded. +*/ +void modc_init(); + +/* + Function: modc_newsnapshot + Called when the client progressed to a new snapshot. + + Remarks: + The client can check for items in the snapshot and perform one time + events like playing sounds, spawning client side effects etc. +*/ +void modc_newsnapshot(); + +/* + Function: modc_entergame + Called when the client has successfully connect to a server and + loaded a map. + + Remarks: + The client can check for items in the map and load them. +*/ +void modc_entergame(); + +/* + Function: modc_shutdown + Called when the client closes down. +*/ +void modc_shutdown(); + +/* + Function: modc_render + Called every frame to let the game render it self. +*/ +void modc_render(); + +/* + Function: modc_statechange + Called every time client changes state. +*/ +void modc_statechange(int new_state, int old_state); + +/* undocumented callbacks */ +void modc_connected(); +void modc_message(int msg); +void modc_predict(); + +void mods_message(int msg, int client_id); +void mods_connected(int client_id); + + +const char *modc_net_version(); +const char *mods_net_version(); + +/* server */ +int server_getclientinfo(int client_id, CLIENT_INFO *info); +const char *server_clientname(int client_id); +void server_setclientname(int client_id, const char *name); + +void server_setbrowseinfo(int game_type, int progression); + +int server_tick(); +int server_tickspeed(); + +/* input */ +int inp_key_was_pressed(int key); +int inp_key_down(int key); +char inp_last_char(); +int inp_last_key(); +void inp_clear(); +void inp_update(); +void inp_init(); +void inp_mouse_mode_absolute(); +void inp_mouse_mode_relative(); +int inp_mouse_doubleclick(); + +int inp_key_presses(int key); +int inp_key_releases(int key); +int inp_key_state(int key); + +const char *inp_key_name(int k); +int inp_key_code(const char *key_name); + +/* message packing */ +enum +{ + MSGFLAG_VITAL=1 +}; + +void msg_pack_start_system(int msg, int flags); +void msg_pack_start(int msg, int flags); +void msg_pack_int(int i); +void msg_pack_string(const char *p, int limit); +void msg_pack_raw(const void *data, int size); +void msg_pack_end(); + +typedef struct +{ + int msg; + int flags; + const unsigned char *data; + int size; +} MSG_INFO; + +const MSG_INFO *msg_get_info(); + +/* message unpacking */ +int msg_unpack_start(const void *data, int data_size, int *system); +int msg_unpack_int(); +const char *msg_unpack_string(); +const unsigned char *msg_unpack_raw(int size); + +/* message sending */ +int server_send_msg(int client_id); /* client_id == -1 == broadcast */ +int client_send_msg(); + +/* client */ +int client_tick(); +int client_predtick(); +float client_intratick(); +float client_intrapredtick(); +int client_tickspeed(); +float client_frametime(); +float client_localtime(); + +int client_state(); +const char *client_error_string(); +int *client_get_input(int tick); +int client_connection_problems(); + +void client_connect(const char *address); +void client_disconnect(); +void client_quit(); +void client_entergame(); + +void client_rcon(const char *cmd); + + +void client_serverbrowse_refresh(int lan); + +SERVER_INFO *client_serverbrowse_sorted_get(int index); +int client_serverbrowse_sorted_num(); + +SERVER_INFO *client_serverbrowse_get(int index); +int client_serverbrowse_num(); + +int client_serverbrowse_num_requests(); + +void client_serverbrowse_update(); + +/* undocumented graphics stuff */ + +void gfx_pretty_text_color(float r, float g, float b, float a); +void gfx_pretty_text(float x, float y, float size, const char *text, int max_width); +float gfx_pretty_text_width(float size, const char *text, int length); + +void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y); +int gfx_memory_usage(); +void gfx_screenshot(); + +void gfx_lines_begin(); +void gfx_lines_draw(float x0, float y0, float x1, float y1); +void gfx_lines_end(); + +void gfx_mask_op(int mask, int write); +void gfx_clear_mask(int fill); + +/* server snap id */ +int snap_new_id(); +void snap_free_id(int id); + +/* other */ +void map_unload_data(int index); +void map_set(void *m); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/e_keynames.c b/src/engine/e_keynames.c new file mode 100644 index 00000000..fa87467d --- /dev/null +++ b/src/engine/e_keynames.c @@ -0,0 +1,524 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ + +#include + +static const char key_strings[512][16] = +{ + "#0", + "#1", + "#2", + "#3", + "#4", + "#5", + "#6", + "#7", + "#8", + "#9", + "#10", + "#11", + "#12", + "#13", + "#14", + "#15", + "#16", + "#17", + "#18", + "#19", + "#20", + "#21", + "#22", + "#23", + "#24", + "#25", + "#26", + "#27", + "#28", + "#29", + "#30", + "#31", + "space", + "#33", + "#34", + "#35", + "#36", + "#37", + "#38", + "#39", + "#40", + "#41", + "#42", + "#43", + "#44", + "#45", + "#46", + "#47", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "#58", + "#59", + "#60", + "#61", + "#62", + "#63", + "#64", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "#91", + "#92", + "#93", + "#94", + "#95", + "#96", + "#97", + "#98", + "#99", + "#100", + "#101", + "#102", + "#103", + "#104", + "#105", + "#106", + "#107", + "#108", + "#109", + "#110", + "#111", + "#112", + "#113", + "#114", + "#115", + "#116", + "#117", + "#118", + "#119", + "#120", + "#121", + "#122", + "#123", + "#124", + "#125", + "#126", + "#127", + "#128", + "#129", + "#130", + "#131", + "#132", + "#133", + "#134", + "#135", + "#136", + "#137", + "#138", + "#139", + "#140", + "#141", + "#142", + "#143", + "#144", + "#145", + "#146", + "#147", + "#148", + "#149", + "#150", + "#151", + "#152", + "#153", + "#154", + "#155", + "#156", + "#157", + "#158", + "#159", + "#160", + "#161", + "#162", + "#163", + "#164", + "#165", + "#166", + "#167", + "#168", + "#169", + "#170", + "#171", + "#172", + "#173", + "#174", + "#175", + "#176", + "#177", + "#178", + "#179", + "#180", + "#181", + "#182", + "#183", + "#184", + "#185", + "#186", + "#187", + "#188", + "#189", + "#190", + "#191", + "#192", + "#193", + "#194", + "#195", + "#196", + "#197", + "#198", + "#199", + "#200", + "#201", + "#202", + "#203", + "#204", + "#205", + "#206", + "#207", + "#208", + "#209", + "#210", + "#211", + "#212", + "#213", + "#214", + "#215", + "#216", + "#217", + "#218", + "#219", + "#220", + "#221", + "#222", + "#223", + "#224", + "#225", + "#226", + "#227", + "#228", + "#229", + "#230", + "#231", + "#232", + "#233", + "#234", + "#235", + "#236", + "#237", + "#238", + "#239", + "#240", + "#241", + "#242", + "#243", + "#244", + "#245", + "#246", + "#247", + "#248", + "#249", + "#250", + "#251", + "#252", + "#253", + "#254", + "#255", + "special", + "esc", + "f1", + "f2", + "f3", + "f4", + "f5", + "f6", + "f7", + "f8", + "f9", + "f10", + "f11", + "f12", + "f13", + "f14", + "f15", + "f16", + "f17", + "f18", + "f19", + "f20", + "f21", + "f22", + "f23", + "f24", + "f25", + "up", + "down", + "left", + "right", + "lshift", + "rshift", + "lctrl", + "rctrl", + "lalt", + "ralt", + "tab", + "enter", + "backspace", + "insert", + "del", + "pageup", + "pagedown", + "home", + "end", + "kp0", + "kp1", + "kp2", + "kp3", + "kp4", + "kp5", + "kp6", + "kp7", + "kp8", + "kp9", + "kpdivide", + "kpmultiply", + "kpsubtract", + "kpadd", + "kpdecimal", + "kpequal", + "kpenter", + "#319", + "#320", + "#321", + "#322", + "#323", + "#324", + "#325", + "#326", + "#327", + "#328", + "#329", + "#330", + "#331", + "#332", + "#333", + "#334", + "#335", + "#336", + "#337", + "#338", + "#339", + "#340", + "#341", + "#342", + "#343", + "#344", + "#345", + "#346", + "#347", + "#348", + "#349", + "#350", + "#351", + "#352", + "#353", + "#354", + "#355", + "#356", + "#357", + "#358", + "#359", + "#360", + "#361", + "#362", + "#363", + "#364", + "#365", + "#366", + "#367", + "#368", + "#369", + "#370", + "#371", + "#372", + "#373", + "#374", + "#375", + "#376", + "#377", + "#378", + "#379", + "#380", + "#381", + "mousewheeldown", + "mousewheelup", + "mouse1", + "mouse2", + "mouse3", + "mouse4", + "mouse5", + "mouse6", + "mouse7", + "mouse8", + "#392", + "#393", + "#394", + "#395", + "#396", + "#397", + "#398", + "#399", + "#400", + "#401", + "#402", + "#403", + "#404", + "#405", + "#406", + "#407", + "#408", + "#409", + "#410", + "#411", + "#412", + "#413", + "#414", + "#415", + "#416", + "#417", + "#418", + "#419", + "#420", + "#421", + "#422", + "#423", + "#424", + "#425", + "#426", + "#427", + "#428", + "#429", + "#430", + "#431", + "#432", + "#433", + "#434", + "#435", + "#436", + "#437", + "#438", + "#439", + "#440", + "#441", + "#442", + "#443", + "#444", + "#445", + "#446", + "#447", + "#448", + "#449", + "#450", + "#451", + "#452", + "#453", + "#454", + "#455", + "#456", + "#457", + "#458", + "#459", + "#460", + "#461", + "#462", + "#463", + "#464", + "#465", + "#466", + "#467", + "#468", + "#469", + "#470", + "#471", + "#472", + "#473", + "#474", + "#475", + "#476", + "#477", + "#478", + "#479", + "#480", + "#481", + "#482", + "#483", + "#484", + "#485", + "#486", + "#487", + "#488", + "#489", + "#490", + "#491", + "#492", + "#493", + "#494", + "#495", + "#496", + "#497", + "#498", + "#499", + "#500", + "#501", + "#502", + "#503", + "#504", + "#505", + "#506", + "#507", + "#508", + "#509", + "#510", + "#511", +}; + +const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; } +int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; } + diff --git a/src/engine/e_keys.h b/src/engine/e_keys.h new file mode 100644 index 00000000..77a52f58 --- /dev/null +++ b/src/engine/e_keys.h @@ -0,0 +1,88 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_KEYS_H +#define ENGINE_KEYS_H +/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ + +enum +{ + KEY_UNKNOWN = -1, + KEY_SPACE = 32, + KEY_SPECIAL = 256, + KEY_ESC = (KEY_SPECIAL+1), + KEY_F1 = (KEY_SPECIAL+2), + KEY_F2 = (KEY_SPECIAL+3), + KEY_F3 = (KEY_SPECIAL+4), + KEY_F4 = (KEY_SPECIAL+5), + KEY_F5 = (KEY_SPECIAL+6), + KEY_F6 = (KEY_SPECIAL+7), + KEY_F7 = (KEY_SPECIAL+8), + KEY_F8 = (KEY_SPECIAL+9), + KEY_F9 = (KEY_SPECIAL+10), + KEY_F10 = (KEY_SPECIAL+11), + KEY_F11 = (KEY_SPECIAL+12), + KEY_F12 = (KEY_SPECIAL+13), + KEY_F13 = (KEY_SPECIAL+14), + KEY_F14 = (KEY_SPECIAL+15), + KEY_F15 = (KEY_SPECIAL+16), + KEY_F16 = (KEY_SPECIAL+17), + KEY_F17 = (KEY_SPECIAL+18), + KEY_F18 = (KEY_SPECIAL+19), + KEY_F19 = (KEY_SPECIAL+20), + KEY_F20 = (KEY_SPECIAL+21), + KEY_F21 = (KEY_SPECIAL+22), + KEY_F22 = (KEY_SPECIAL+23), + KEY_F23 = (KEY_SPECIAL+24), + KEY_F24 = (KEY_SPECIAL+25), + KEY_F25 = (KEY_SPECIAL+26), + KEY_UP = (KEY_SPECIAL+27), + KEY_DOWN = (KEY_SPECIAL+28), + KEY_LEFT = (KEY_SPECIAL+29), + KEY_RIGHT = (KEY_SPECIAL+30), + KEY_LSHIFT = (KEY_SPECIAL+31), + KEY_RSHIFT = (KEY_SPECIAL+32), + KEY_LCTRL = (KEY_SPECIAL+33), + KEY_RCTRL = (KEY_SPECIAL+34), + KEY_LALT = (KEY_SPECIAL+35), + KEY_RALT = (KEY_SPECIAL+36), + KEY_TAB = (KEY_SPECIAL+37), + KEY_ENTER = (KEY_SPECIAL+38), + KEY_BACKSPACE = (KEY_SPECIAL+39), + KEY_INSERT = (KEY_SPECIAL+40), + KEY_DEL = (KEY_SPECIAL+41), + KEY_PAGEUP = (KEY_SPECIAL+42), + KEY_PAGEDOWN = (KEY_SPECIAL+43), + KEY_HOME = (KEY_SPECIAL+44), + KEY_END = (KEY_SPECIAL+45), + KEY_KP_0 = (KEY_SPECIAL+46), + KEY_KP_1 = (KEY_SPECIAL+47), + KEY_KP_2 = (KEY_SPECIAL+48), + KEY_KP_3 = (KEY_SPECIAL+49), + KEY_KP_4 = (KEY_SPECIAL+50), + KEY_KP_5 = (KEY_SPECIAL+51), + KEY_KP_6 = (KEY_SPECIAL+52), + KEY_KP_7 = (KEY_SPECIAL+53), + KEY_KP_8 = (KEY_SPECIAL+54), + KEY_KP_9 = (KEY_SPECIAL+55), + KEY_KP_DIVIDE = (KEY_SPECIAL+56), + KEY_KP_MULTIPLY = (KEY_SPECIAL+57), + KEY_KP_SUBTRACT = (KEY_SPECIAL+58), + KEY_KP_ADD = (KEY_SPECIAL+59), + KEY_KP_DECIMAL = (KEY_SPECIAL+60), + KEY_KP_EQUAL = (KEY_SPECIAL+61), + KEY_KP_ENTER = (KEY_SPECIAL+62), + KEY_REPEAT = 0x00030005, + KEY_MOUSE_WHEEL_DOWN = 382, + KEY_MOUSE_WHEEL_UP = 383, + KEY_MOUSE_FIRST = 384, + KEY_MOUSE_1 = KEY_MOUSE_FIRST+0, + KEY_MOUSE_2 = KEY_MOUSE_FIRST+1, + KEY_MOUSE_3 = KEY_MOUSE_FIRST+2, + KEY_MOUSE_4 = KEY_MOUSE_FIRST+3, + KEY_MOUSE_5 = KEY_MOUSE_FIRST+4, + KEY_MOUSE_6 = KEY_MOUSE_FIRST+5, + KEY_MOUSE_7 = KEY_MOUSE_FIRST+6, + KEY_MOUSE_8 = KEY_MOUSE_FIRST+7, + KEY_LAST +}; + +#endif diff --git a/src/engine/e_map.c b/src/engine/e_map.c new file mode 100644 index 00000000..6dfa4aa6 --- /dev/null +++ b/src/engine/e_map.c @@ -0,0 +1,61 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include "e_datafile.h" + +static DATAFILE *map = 0; + +void *map_get_data(int index) +{ + return datafile_get_data(map, index); +} + +void map_unload_data(int index) +{ + datafile_unload_data(map, index); +} + +void *map_get_item(int index, int *type, int *id) +{ + return datafile_get_item(map, index, type, id); +} + +void map_get_type(int type, int *start, int *num) +{ + datafile_get_type(map, type, start, num); +} + +void *map_find_item(int type, int id) +{ + return datafile_find_item(map, type, id); +} + +int map_num_items() +{ + return datafile_num_items(map); +} + +void map_unload() +{ + datafile_unload(map); + map = 0x0; +} + +int map_is_loaded() +{ + return map != 0; +} + +int map_load(const char *mapname) +{ + char buf[512]; + sprintf(buf, "data/maps/%s.map", mapname); + map = datafile_load(buf); + return map != 0; +} + +void map_set(void *m) +{ + if(map) + map_unload(); + map = (DATAFILE*)m; +} diff --git a/src/engine/e_memheap.c b/src/engine/e_memheap.c new file mode 100644 index 00000000..6d117631 --- /dev/null +++ b/src/engine/e_memheap.c @@ -0,0 +1,102 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" + +typedef struct CHUNK_t +{ + char *memory; + char *current; + char *end; + struct CHUNK_t *next; +} CHUNK; + +typedef struct +{ + CHUNK *current; +} HEAP; + +/* how large each chunk should be */ +static const int chunksize = 1024*64; + +/* allocates a new chunk to be used */ +static CHUNK *memheap_newchunk() +{ + CHUNK *chunk; + char *mem; + + /* allocate memory */ + mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1); + if(!mem) + return 0x0; + + /* the chunk structure is located in the begining of the chunk */ + /* init it and return the chunk */ + chunk = (CHUNK*)mem; + chunk->memory = (char*)(chunk+1); + chunk->current = chunk->memory; + chunk->end = chunk->memory + chunksize; + chunk->next = (CHUNK *)0x0; + return chunk; +} + +/******************/ +static void *memheap_allocate_from_chunk(CHUNK *chunk, int size) +{ + char *mem; + + /* check if we need can fit the allocation */ + if(chunk->current + size >= chunk->end) + return (void*)0x0; + + /* get memory and move the pointer forward */ + mem = chunk->current; + chunk->current += size; + return mem; +} + +/* creates a heap */ +HEAP *memheap_create() +{ + CHUNK *chunk; + HEAP *heap; + + /* allocate a chunk and allocate the heap structure on that chunk */ + chunk = memheap_newchunk(); + heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP)); + heap->current = chunk; + return heap; +} + +/* destroys the heap */ +void memheap_destroy(HEAP *heap) +{ + CHUNK *chunk = heap->current; + CHUNK *next; + + while(chunk) + { + next = chunk->next; + mem_free(chunk); + chunk = next; + } +} + +/* */ +void *memheap_allocate(HEAP *heap, int size) +{ + char *mem; + + /* try to allocate from current chunk */ + mem = (char *)memheap_allocate_from_chunk(heap->current, size); + if(!mem) + { + /* allocate new chunk and add it to the heap */ + CHUNK *chunk = memheap_newchunk(); + chunk->next = heap->current; + heap->current = chunk; + + /* try to allocate again */ + mem = (char *)memheap_allocate_from_chunk(heap->current, size); + } + + return mem; +} diff --git a/src/engine/e_memheap.h b/src/engine/e_memheap.h new file mode 100644 index 00000000..83082046 --- /dev/null +++ b/src/engine/e_memheap.h @@ -0,0 +1,6 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +typedef struct HEAP_t HEAP; +HEAP *memheap_create(); +void memheap_destroy(HEAP *heap); +void *memheap_allocate(HEAP *heap, int size); diff --git a/src/engine/e_msg.c b/src/engine/e_msg.c new file mode 100644 index 00000000..adfb1cd3 --- /dev/null +++ b/src/engine/e_msg.c @@ -0,0 +1,55 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_interface.h" +#include "e_packer.h" + +/* message packing */ +static PACKER msg_packer; +static MSG_INFO pack_info; + +void msg_pack_int(int i) { packer_add_int(&msg_packer, i); } +void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); } +void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); } + +void msg_pack_start_system(int msg, int flags) +{ + packer_reset(&msg_packer); + pack_info.msg = (msg<<1)|1; + pack_info.flags = flags; + + msg_pack_int(pack_info.msg); +} + +void msg_pack_start(int msg, int flags) +{ + packer_reset(&msg_packer); + pack_info.msg = msg<<1; + pack_info.flags = flags; + + msg_pack_int(pack_info.msg); +} + +void msg_pack_end() +{ + pack_info.size = packer_size(&msg_packer); + pack_info.data = packer_data(&msg_packer); +} + +const MSG_INFO *msg_get_info() +{ + return &pack_info; +} + +/* message unpacking */ +static UNPACKER msg_unpacker; +int msg_unpack_start(const void *data, int data_size, int *system) +{ + int msg; + unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size); + msg = msg_unpack_int(); + *system = msg&1; + return msg>>1; +} + +int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); } +const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); } +const unsigned char *msg_unpack_raw(int size) { return unpacker_get_raw(&msg_unpacker, size); } diff --git a/src/engine/e_network.c b/src/engine/e_network.c new file mode 100644 index 00000000..b26e75f0 --- /dev/null +++ b/src/engine/e_network.c @@ -0,0 +1,937 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include + +#include "e_system.h" +#include "e_config.h" +#include "e_network.h" + +/* + header (6 bytes) + unsigned char flags; 1 + unsigned char seq_ack[3]; 4 + unsigned char token[2]; 6 +*/ + +enum +{ + NETWORK_VERSION = 1, + + NETWORK_HEADER_SIZE = 6, + NETWORK_MAX_PAYLOAD = 1024, + NETWORK_MAX_PACKET_SIZE = NETWORK_HEADER_SIZE+NETWORK_MAX_PAYLOAD, + NETWORK_MAX_CLIENTS = 16, + + NETWORK_CONNSTATE_OFFLINE=0, + NETWORK_CONNSTATE_CONNECT=1, + NETWORK_CONNSTATE_CONNECTACCEPTED=2, + NETWORK_CONNSTATE_ONLINE=3, + NETWORK_CONNSTATE_ERROR=4, + + NETWORK_PACKETFLAG_CONNECT=0x01, + NETWORK_PACKETFLAG_ACCEPT=0x02, + NETWORK_PACKETFLAG_CLOSE=0x04, + NETWORK_PACKETFLAG_VITAL=0x08, + NETWORK_PACKETFLAG_RESEND=0x10, + NETWORK_PACKETFLAG_CONNLESS=0x20, + + NETWORK_MAX_SEQACK=0x1000 +}; + +static int current_token = 1; + +typedef struct +{ + unsigned char ID[2]; + unsigned char version; + unsigned char flags; + unsigned short seq; + unsigned short ack; + unsigned crc; + int token; + unsigned data_size; + int64 first_send_time; + unsigned char *data; +} NETPACKETDATA; + + +static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet) +{ + unsigned char buffer[NETWORK_MAX_PACKET_SIZE]; + int send_size = NETWORK_HEADER_SIZE+packet->data_size; + + buffer[0] = packet->flags; + buffer[1] = ((packet->seq>>4)&0xf0) | ((packet->ack>>8)&0x0f); + buffer[2] = packet->seq; + buffer[3] = packet->ack; + buffer[4] = packet->token>>8; + buffer[5] = packet->token&0xff; + mem_copy(buffer+NETWORK_HEADER_SIZE, packet->data, packet->data_size); + net_udp4_send(socket, addr, buffer, send_size); +} + +typedef struct RINGBUFFER_ITEM_t +{ + struct RINGBUFFER_ITEM_t *next; + struct RINGBUFFER_ITEM_t *prev; + int size; +} RINGBUFFER_ITEM; + +typedef struct +{ + RINGBUFFER_ITEM *first; + RINGBUFFER_ITEM *last; + unsigned buffer_size; +} RINGBUFFER; + +static void rb_init(RINGBUFFER *rb) +{ + rb->first = 0; + rb->last = 0; + rb->buffer_size = 0; +} + +static void *rb_item_data(RINGBUFFER_ITEM *item) +{ + return (void*)(item+1); +} + +static void *rb_alloc(RINGBUFFER *rb, int size) +{ + RINGBUFFER_ITEM *item = (RINGBUFFER_ITEM*)mem_alloc(sizeof(RINGBUFFER_ITEM)+size, 1); + item->size = size; + + item->prev = rb->last; + item->next = 0; + if(rb->last) + rb->last->next = item; + else + rb->first = item; + rb->last = item; + + rb->buffer_size += size; + return rb_item_data(item); +} + +static void rb_pop_first(RINGBUFFER *rb) +{ + if(rb->first) + { + RINGBUFFER_ITEM *next = rb->first->next; + rb->buffer_size -= rb->first->size; + mem_free(rb->first); + rb->first = next; + if(rb->first) + rb->first->prev = NULL; + else + rb->last = NULL; + } +} + +static void rb_clear(RINGBUFFER *rb) +{ + while(rb->first) + rb_pop_first(rb); +} + + +typedef struct +{ + unsigned short seq; + unsigned short ack; + unsigned state; + + int token; + + int remote_closed; + + int connected; + int disconnected; + + RINGBUFFER buffer; + + int64 last_update_time; + int64 last_recv_time; + int64 last_send_time; + + char error_string[256]; + + NETADDR4 peeraddr; + NETSOCKET socket; + NETSTATS stats; +} NETCONNECTION; + +typedef struct +{ + NETCONNECTION conn; +} NETSLOT; + +struct NETSERVER_t +{ + 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]; +} ; + +struct NETCLIENT_t +{ + NETADDR4 server_addr; + NETSOCKET socket; + unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE]; + + NETCONNECTION conn; +}; + +static void conn_reset_stats(NETCONNECTION *conn) +{ + mem_zero(&conn->stats, sizeof(conn->stats)); +} + +static void conn_reset(NETCONNECTION *conn) +{ + conn->seq = 0; + conn->ack = 0; + conn->remote_closed = 0; + + if(conn->state == NETWORK_CONNSTATE_ONLINE || + conn->state == NETWORK_CONNSTATE_ERROR) + { + conn->disconnected++; + } + + conn->state = NETWORK_CONNSTATE_OFFLINE; + conn->last_send_time = 0; + conn->last_recv_time = 0; + conn->last_update_time = 0; + conn->token = -1; + + rb_clear(&conn->buffer); +} + + +static const char *conn_error(NETCONNECTION *conn) +{ + return conn->error_string; +} + +static void conn_set_error(NETCONNECTION *conn, const char *str) +{ + strcpy(conn->error_string, str); +} + +/* +static int conn_state(NETCONNECTION *conn) +{ + return conn->state; +}*/ + +static void conn_init(NETCONNECTION *conn, NETSOCKET socket) +{ + conn_reset(conn); + conn_reset_stats(conn); + conn->socket = socket; + conn->connected = 0; + conn->disconnected = 0; + rb_init(&conn->buffer); + mem_zero(conn->error_string, sizeof(conn->error_string)); +} + +static void conn_ack(NETCONNECTION *conn, int ack) +{ + while(1) + { + RINGBUFFER_ITEM *item = conn->buffer.first; + NETPACKETDATA *resend; + if(!item) + break; + + resend = (NETPACKETDATA *)rb_item_data(item); + if(resend->seq <= ack || (ack < NETWORK_MAX_SEQACK/3 && resend->seq > NETWORK_MAX_SEQACK/2)) + rb_pop_first(&conn->buffer); + else + break; + } +} + +static void conn_send_raw(NETCONNECTION *conn, NETPACKETDATA *data) +{ + conn->last_send_time = time_get(); + conn->stats.send_packets++; + conn->stats.send_bytes += data->data_size + NETWORK_HEADER_SIZE; + send_packet(conn->socket, &conn->peeraddr, data); +} + +static void conn_resend(NETCONNECTION *conn) +{ + RINGBUFFER_ITEM *item = conn->buffer.first; + while(item) + { + NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); + conn->stats.resend_packets++; + conn->stats.resend_bytes += resend->data_size + NETWORK_HEADER_SIZE; + conn_send_raw(conn, resend); + item = item->next; + } +} + +static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data) +{ + NETPACKETDATA p; + + if(flags&NETWORK_PACKETFLAG_VITAL) + conn->seq = (conn->seq+1)%NETWORK_MAX_SEQACK; + + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = flags; + p.seq = conn->seq; + p.ack = conn->ack; + p.crc = 0; + p.token = conn->token; + p.data_size = data_size; + p.data = (unsigned char *)data; + p.first_send_time = time_get(); + + if(flags&NETWORK_PACKETFLAG_VITAL) + { + /* save packet if we need to resend */ + NETPACKETDATA *resend = (NETPACKETDATA *)rb_alloc(&conn->buffer, sizeof(NETPACKETDATA)+data_size); + *resend = p; + resend->data = (unsigned char *)(resend+1); + mem_copy(resend->data, p.data, p.data_size); + } + + /* TODO: calc crc */ + conn_send_raw(conn, &p); +} + +static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr) +{ + if(conn->state != NETWORK_CONNSTATE_OFFLINE) + return -1; + + /* init connection */ + conn_reset(conn); + conn->peeraddr = *addr; + conn->token = current_token++; + mem_zero(conn->error_string, sizeof(conn->error_string)); + conn->state = NETWORK_CONNSTATE_CONNECT; + conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); + return 0; +} + +static void conn_disconnect(NETCONNECTION *conn, const char *reason) +{ + 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->error_string[0] = 0; + if(reason) + strcpy(conn->error_string, reason); + } + + conn_reset(conn); +} + +static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) +{ + conn->last_recv_time = time_get(); + conn->stats.recv_packets++; + conn->stats.recv_bytes += p->data_size + NETWORK_HEADER_SIZE; + + if(p->flags&NETWORK_PACKETFLAG_CLOSE) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn->remote_closed = 1; + + if(p->data_size) + conn_set_error(conn, (char *)p->data); + else + conn_set_error(conn, "no reason given"); + if(config.debug) + dbg_msg("conn", "closed reason='%s'", conn_error(conn)); + return 0; + } + + if(conn->state == NETWORK_CONNSTATE_OFFLINE) + { + if(p->flags == NETWORK_PACKETFLAG_CONNECT) + { + /* send response and init connection */ + conn->state = NETWORK_CONNSTATE_ONLINE; + conn->connected++; + conn->peeraddr = *addr; + conn->token = p->token; + conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); + if(config.debug) + dbg_msg("connection", "got connection, sending connect+accept"); + } + } + else if(net_addr4_cmp(&conn->peeraddr, addr) == 0) + { + if(p->token != conn->token) + return 0; + + if(conn->state == NETWORK_CONNSTATE_ONLINE) + { + /* remove packages that are acked */ + conn_ack(conn, p->ack); + + /* check if resend is requested */ + if(p->flags&NETWORK_PACKETFLAG_RESEND) + conn_resend(conn); + + if(p->flags&NETWORK_PACKETFLAG_VITAL) + { + if(p->seq == (conn->ack+1)%NETWORK_MAX_SEQACK) + { + /* in sequence */ + conn->ack = (conn->ack+1)%NETWORK_MAX_SEQACK; + } + else + { + /* out of sequence, request 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; + } + } + else + { + if(p->seq > conn->ack) + conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0); + } + + if(p->data_size == 0) + return 0; + + return 1; + } + else if(conn->state == NETWORK_CONNSTATE_CONNECT) + { + /* connection made */ + if(p->flags == (NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT)) + { + conn_send(conn, NETWORK_PACKETFLAG_ACCEPT, 0, 0); + conn->state = NETWORK_CONNSTATE_ONLINE; + conn->connected++; + dbg_msg("connection", "got connect+accept, sending accept. connection online"); + } + } + /* + else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) + { + // connection made + if(p->flags == NETWORK_PACKETFLAG_ACCEPT) + { + conn->state = NETWORK_CONNSTATE_ONLINE; + dbg_msg("connection", "got accept. connection online"); + } + }*/ + else + { + /* strange packet, wrong state */ + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "strange state and packet"); + } + } + else + { + /* strange packet, not ment for me */ + } + + return 0; +} + + + +static int conn_update(NETCONNECTION *conn) +{ + int64 now = time_get(); + + if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR) + return 0; + + /* watch out for major hitches */ + { + int64 delta = now-conn->last_update_time; + if(conn->last_update_time && delta > time_freq()/2) + { + RINGBUFFER_ITEM *item = conn->buffer.first; + + dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq())); + conn->last_recv_time += delta; + + while(item) + { + NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); + resend->first_send_time += delta; + item = item->next; + } + } + + conn->last_update_time = now; + } + + + /* check for timeout */ + if(conn->state != NETWORK_CONNSTATE_OFFLINE && + conn->state != NETWORK_CONNSTATE_CONNECT && + (now-conn->last_recv_time) > time_freq()*10) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "timeout"); + } + + /* check for large buffer errors */ + if(conn->buffer.buffer_size > 1024*64) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "too weak connection (out of buffer)"); + } + + if(conn->buffer.first) + { + NETPACKETDATA *resend = (NETPACKETDATA *)(conn->buffer.first+1); + if(now-resend->first_send_time > time_freq()*10) + { + conn->state = NETWORK_CONNSTATE_ERROR; + conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); + } + } + + /* send keep alives if nothing has happend for 250ms */ + if(conn->state == NETWORK_CONNSTATE_ONLINE) + { + if(time_get()-conn->last_send_time> time_freq()/4) + conn_send(conn, NETWORK_PACKETFLAG_VITAL, 0, 0); + } + else if(conn->state == NETWORK_CONNSTATE_CONNECT) + { + if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ + conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); + } + else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) + { + if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ + conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); + } + + return 0; +} + + +static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet) +{ + /* check the size */ + if(size < NETWORK_HEADER_SIZE || size > NETWORK_MAX_PACKET_SIZE) + return -1; + + /* read the packet */ + packet->ID[0] = 'T'; + packet->ID[1] = 'W'; + packet->version = NETWORK_VERSION; + packet->flags = buffer[0]; + packet->seq = ((buffer[1]&0xf0)<<4)|buffer[2]; + packet->ack = ((buffer[1]&0x0f)<<8)|buffer[3]; + packet->crc = 0; + packet->token = (buffer[4]<<8)|buffer[5]; + packet->data_size = size - NETWORK_HEADER_SIZE; + packet->data = buffer+NETWORK_HEADER_SIZE; + + /* check the packet */ + if(packet->ID[0] != 'T' || packet->ID[1] != 'W') + return 1; + + if(packet->version != NETWORK_VERSION) + return 1; + + /* TODO: perform crc check */ + + /* return success */ + return 0; +} + +NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags) +{ + int i; + NETSERVER *server; + NETSOCKET socket = net_udp4_create(bindaddr); + if(socket == NETSOCKET_INVALID) + return 0; + + server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); + mem_zero(server, sizeof(NETSERVER)); + 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(i = 0; i < NETWORK_MAX_CLIENTS; i++) + conn_init(&server->slots[i].conn, server->socket); + + return server; +} + +int netserver_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 netserver_max_clients(NETSERVER *s) +{ + return s->max_clients; +} + +int netserver_close(NETSERVER *s) +{ + /* TODO: implement me */ + return 0; +} + +int netserver_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); + + return 0; +} + +int netserver_update(NETSERVER *s) +{ + int i; + for(i = 0; i < s->max_clients; i++) + { + conn_update(&s->slots[i].conn); + if(s->slots[i].conn.state == NETWORK_CONNSTATE_ERROR) + netserver_drop(s, i, conn_error(&s->slots[i].conn)); + } + return 0; +} + +int netserver_recv(NETSERVER *s, NETPACKET *packet) +{ + NETPACKETDATA data; + int i, r, bytes, found; + NETADDR4 addr; + + while(1) + { + bytes = net_udp4_recv(s->socket, &addr, s->recv_buffer, NETWORK_MAX_PACKET_SIZE); + + /* no more packets for now */ + if(bytes <= 0) + break; + + r = check_packet(s->recv_buffer, bytes, &data); + if(r == 0) + { + if(data.flags&NETWORK_PACKETFLAG_CONNLESS) + { + /* connection less packets */ + packet->client_id = -1; + packet->address = addr; + packet->flags = PACKETFLAG_CONNLESS; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + /* ok packet, process it */ + if(data.flags == NETWORK_PACKETFLAG_CONNECT) + { + found = 0; + + /* check if we already got this client */ + for(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) + { + found = 1; /* silent ignore.. we got this client already */ + break; + } + } + + /* client that wants to connect */ + if(!found) + { + for(i = 0; i < s->max_clients; i++) + { + if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE) + { + found = 1; + conn_feed(&s->slots[i].conn, &data, &addr); + if(s->new_client) + s->new_client(i, s->user_ptr); + break; + } + } + } + + if(!found) + { + /* 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(i = 0; i < s->max_clients; i++) + { + if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) + { + if(conn_feed(&s->slots[i].conn, &data, &addr)) + { + if(data.data_size) + { + packet->client_id = i; + packet->address = addr; + packet->flags = 0; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + } + } + } + } + } + } + else + { + /* errornous packet, drop it */ + dbg_msg("server", "crazy packet"); + } + + /* read header */ + /* do checksum */ + } + + return 0; +} + +int netserver_send(NETSERVER *s, NETPACKET *packet) +{ + dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); + + if(packet->flags&PACKETFLAG_CONNLESS) + { + /* send connectionless packet */ + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CONNLESS; + p.seq = 0; + p.ack = 0; + p.crc = 0; + p.data_size = packet->data_size; + p.data = (unsigned char *)packet->data; + send_packet(s->socket, &packet->address, &p); + } + else + { + int flags = 0; + dbg_assert(packet->client_id >= 0, "errornous client id"); + dbg_assert(packet->client_id < s->max_clients, "errornous client id"); + if(packet->flags&PACKETFLAG_VITAL) + flags |= NETWORK_PACKETFLAG_VITAL; + conn_send(&s->slots[packet->client_id].conn, flags, packet->data_size, packet->data); + } + return 0; +} + +void netserver_stats(NETSERVER *s, NETSTATS *stats) +{ + int num_stats = sizeof(NETSTATS)/sizeof(int); + int *istats = (int *)stats; + int c, i; + + mem_zero(stats, sizeof(NETSTATS)); + + for(c = 0; c < s->max_clients; c++) + { + if(s->slots[c].conn.state != NETWORK_CONNSTATE_OFFLINE) + { + int *sstats = (int *)(&(s->slots[c].conn.stats)); + for(i = 0; i < num_stats; i++) + istats[i] += sstats[i]; + } + } +} + +NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags) +{ + NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); + mem_zero(client, sizeof(NETCLIENT)); + client->socket = net_udp4_create(bindaddr); + conn_init(&client->conn, client->socket); + return client; +} + +int netclient_close(NETCLIENT *c) +{ + /* TODO: implement me */ + return 0; +} + +int netclient_update(NETCLIENT *c) +{ + conn_update(&c->conn); + if(c->conn.state == NETWORK_CONNSTATE_ERROR) + netclient_disconnect(c, conn_error(&c->conn)); + return 0; +} + +int netclient_disconnect(NETCLIENT *c, const char *reason) +{ + dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); + conn_disconnect(&c->conn, reason); + return 0; +} + +int netclient_connect(NETCLIENT *c, NETADDR4 *addr) +{ + conn_connect(&c->conn, addr); + return 0; +} + +int netclient_recv(NETCLIENT *c, NETPACKET *packet) +{ + while(1) + { + NETADDR4 addr; + NETPACKETDATA data; + int r; + int bytes = net_udp4_recv(c->socket, &addr, c->recv_buffer, NETWORK_MAX_PACKET_SIZE); + + /* no more packets for now */ + if(bytes <= 0) + break; + + r = check_packet(c->recv_buffer, bytes, &data); + + if(r == 0) + { + if(data.flags&NETWORK_PACKETFLAG_CONNLESS) + { + /* connection less packets */ + packet->client_id = -1; + packet->address = addr; + packet->flags = PACKETFLAG_CONNLESS; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + if(conn_feed(&c->conn, &data, &addr)) + { + /* fill in packet */ + packet->client_id = 0; + packet->address = addr; + packet->flags = 0; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + /* errornous packet, drop it */ + } + } + } + } + + return 0; +} + +int netclient_send(NETCLIENT *c, NETPACKET *packet) +{ + dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); + + if(packet->flags&PACKETFLAG_CONNLESS) + { + /* send connectionless packet */ + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CONNLESS; + 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); + } + else + { + int flags = 0; + dbg_assert(packet->client_id == 0, "errornous client id"); + if(packet->flags&PACKETFLAG_VITAL) + flags |= NETWORK_PACKETFLAG_VITAL; + conn_send(&c->conn, flags, packet->data_size, packet->data); + } + return 0; +} + +int netclient_state(NETCLIENT *c) +{ + if(c->conn.state == NETWORK_CONNSTATE_ONLINE) + return NETSTATE_ONLINE; + if(c->conn.state == NETWORK_CONNSTATE_OFFLINE) + return NETSTATE_OFFLINE; + return NETSTATE_CONNECTING; +} + +int netclient_gotproblems(NETCLIENT *c) +{ + if(time_get() - c->conn.last_recv_time > time_freq()) + return 1; + return 0; +} + +void netclient_stats(NETCLIENT *c, NETSTATS *stats) +{ + *stats = c->conn.stats; +} + +const char *netclient_error_string(NETCLIENT *c) +{ + return conn_error(&c->conn); +} diff --git a/src/engine/e_network.h b/src/engine/e_network.h new file mode 100644 index 00000000..338c5982 --- /dev/null +++ b/src/engine/e_network.h @@ -0,0 +1,113 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +typedef struct +{ + /* -1 means that it's a stateless packet */ + /* 0 on the client means the server */ + int client_id; + NETADDR4 address; /* only used when client_id == -1 */ + int flags; + int data_size; + const void *data; +} NETPACKET; + +typedef struct +{ + int send_bytes; + int recv_bytes; + int send_packets; + int recv_packets; + + int resend_packets; + int resend_bytes; +} NETSTATS; + +typedef struct NETSERVER_t NETSERVER; +typedef struct NETCLIENT_t NETCLIENT; + +enum +{ + NETFLAG_ALLOWSTATELESS=1, + PACKETFLAG_VITAL=1, + PACKETFLAG_CONNLESS=2, + + NETSTATE_OFFLINE=0, + NETSTATE_CONNECTING, + NETSTATE_ONLINE +}; + +typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); +typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); + +/* server side */ +NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags); +int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user); +int netserver_recv(NETSERVER *s, NETPACKET *packet); +int netserver_send(NETSERVER *s, NETPACKET *packet); +int netserver_close(NETSERVER *s); +int netserver_update(NETSERVER *s); +int netserver_drop(NETSERVER *s, int client_id, const char *reason); +int netserver_max_clients(NETSERVER *s); +void netserver_stats(NETSERVER *s, NETSTATS *stats); + +/* client side */ +NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags); +int netclient_disconnect(NETCLIENT *c, const char *reason); +int netclient_connect(NETCLIENT *c, NETADDR4 *addr); +int netclient_recv(NETCLIENT *c, NETPACKET *packet); +int netclient_send(NETCLIENT *c, NETPACKET *packet); +int netclient_close(NETCLIENT *c); +int netclient_update(NETCLIENT *c); +int netclient_state(NETCLIENT *c); +int netclient_gotproblems(NETCLIENT *c); +void netclient_stats(NETCLIENT *c, NETSTATS *stats); +const char *netclient_error_string(NETCLIENT *c); + +#ifdef __cplusplus +class net_server +{ + NETSERVER *ptr; +public: + net_server() : ptr(0) {} + ~net_server() { close(); } + + int open(NETADDR4 bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; } + int close() { int r = netserver_close(ptr); ptr = 0; return r; } + + int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) + { return netserver_set_callbacks(ptr, new_client, del_client, user); } + + int recv(NETPACKET *packet) { return netserver_recv(ptr, packet); } + int send(NETPACKET *packet) { return netserver_send(ptr, packet); } + int update() { return netserver_update(ptr); } + + int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); } + + int max_clients() { return netserver_max_clients(ptr); } + void stats(NETSTATS *stats) { netserver_stats(ptr, stats); } +}; + + +class net_client +{ + NETCLIENT *ptr; +public: + net_client() : ptr(0) {} + ~net_client() { close(); } + + int open(NETADDR4 bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; } + int close() { int r = netclient_close(ptr); ptr = 0; return r; } + + int connect(NETADDR4 *addr) { return netclient_connect(ptr, addr); } + int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); } + + int recv(NETPACKET *packet) { return netclient_recv(ptr, packet); } + int send(NETPACKET *packet) { return netclient_send(ptr, packet); } + int update() { return netclient_update(ptr); } + + const char *error_string() { return netclient_error_string(ptr); } + + int state() { return netclient_state(ptr); } + void stats(NETSTATS *stats) { netclient_stats(ptr, stats); } +}; +#endif diff --git a/src/engine/e_packer.c b/src/engine/e_packer.c new file mode 100644 index 00000000..948db4ea --- /dev/null +++ b/src/engine/e_packer.c @@ -0,0 +1,91 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include "e_packer.h" +#include "e_compression.h" + +void packer_reset(PACKER *p) +{ + p->error = 0; + p->current = p->buffer; + p->end = p->current + PACKER_BUFFER_SIZE; +} + +void packer_add_int(PACKER *p, int i) +{ + p->current = vint_pack(p->current, i); +} + +void packer_add_string(PACKER *p, const char *str, int limit) +{ + if(limit > 0) + { + while(*str && limit != 0) + { + *p->current++ = *str++; + limit--; + } + *p->current++ = 0; + } + else + { + while(*str) + *p->current++ = *str++; + *p->current++ = 0; + } +} + +void packer_add_raw(PACKER *p, const unsigned char *data, int size) +{ + while(size) + { + *p->current++ = *data++; + size--; + } +} + +int packer_size(PACKER *p) +{ + return (const unsigned char *)p->current-(const unsigned char *)p->buffer; +} + +const unsigned char *packer_data(PACKER *p) +{ + return (const unsigned char *)p->buffer; +} + +void unpacker_reset(UNPACKER *p, const unsigned char *data, int size) +{ + p->error = 0; + p->start = data; + p->end = p->start + size; + p->current = p->start; +} + +int unpacker_get_int(UNPACKER *p) +{ + int i; + if(p->current >= p->end) + return 0; + p->current = vint_unpack(p->current, &i); + return i; +} + +const char *unpacker_get_string(UNPACKER *p) +{ + const char *ptr; + if(p->current >= p->end) + return ""; + + ptr = (const char *)p->current; + while(*p->current) /* skip the string */ + p->current++; + p->current++; + return ptr; +} + +const unsigned char *unpacker_get_raw(UNPACKER *p, int size) +{ + const unsigned char *ptr = p->current; + p->current += size; + return ptr; +} diff --git a/src/engine/e_packer.h b/src/engine/e_packer.h new file mode 100644 index 00000000..71b99ff7 --- /dev/null +++ b/src/engine/e_packer.h @@ -0,0 +1,35 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +enum +{ + PACKER_BUFFER_SIZE=1024*2 +}; + +typedef struct +{ + + unsigned char buffer[PACKER_BUFFER_SIZE]; + unsigned char *current; + unsigned char *end; + int error; +} PACKER; + +typedef struct +{ + const unsigned char *current; + const unsigned char *start; + const unsigned char *end; + int error; +} UNPACKER; + +void packer_reset(PACKER *p); +void packer_add_int(PACKER *p, int i); +void packer_add_string(PACKER *p, const char *str, int limit); +void packer_add_raw(PACKER *p, const unsigned char *data, int size); +int packer_size(PACKER *p); +const unsigned char *packer_data(PACKER *p); + +void unpacker_reset(UNPACKER *p, const unsigned char *data, int size); +int unpacker_get_int(UNPACKER *p); +const char *unpacker_get_string(UNPACKER *p); +const unsigned char *unpacker_get_raw(UNPACKER *p, int size); diff --git a/src/engine/e_protocol.h b/src/engine/e_protocol.h new file mode 100644 index 00000000..0e8a63f4 --- /dev/null +++ b/src/engine/e_protocol.h @@ -0,0 +1,60 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_system.h" + +/* + Connection diagram - How the initilization works. + + Client -> INFO -> Server + Contains version info, name, and some other info. + + Client <- MAP <- Server + Contains current map. + + Client -> READY -> Server + The client has loaded the map and is ready to go, + but the mod needs to send it's information aswell. + modc_connected is called on the client and + mods_connected is called on the server. + The client should call client_entergame when the + mod has done it's initilization. + + Client -> ENTERGAME -> Server + Tells the server to start sending snapshots. + client_entergame and server_client_enter is called. +*/ + + +enum +{ + NETMSG_NULL=0, + + /* the first thing sent by the client + contains the version info for the client */ + NETMSG_INFO=1, + + /* sent by server */ + NETMSG_MAP, + NETMSG_SNAP, + NETMSG_SNAPEMPTY, + NETMSG_SNAPSINGLE, + NETMSG_SNAPSMALL, + + /* sent by client */ + NETMSG_READY, + NETMSG_ENTERGAME, + NETMSG_INPUT, + NETMSG_CMD, + + /* sent by both */ + NETMSG_ERROR +}; + + +/* this should be revised */ +enum +{ + MAX_NAME_LENGTH=32, + MAX_CLANNAME_LENGTH=32, + MAX_INPUT_SIZE=128, + MAX_SNAPSHOT_PACKSIZE=900 +}; diff --git a/src/engine/e_snapshot.c b/src/engine/e_snapshot.c new file mode 100644 index 00000000..91b7797b --- /dev/null +++ b/src/engine/e_snapshot.c @@ -0,0 +1,448 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_snapshot.h" +#include "e_compression.h" + + +int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); } +int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; } +int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; } +int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; } + +int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); } +char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); } + +SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index) +{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); } + +int snapshot_get_item_datasize(SNAPSHOT *snap, int index) +{ + if(index == snap->num_items-1) + return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); + return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); +} + +int snapshot_get_item_index(SNAPSHOT *snap, int key) +{ + /* TODO: OPT: this should not be a linear search. very bad */ + int i; + for(i = 0; i < snap->num_items; i++) + { + if(snapitem_key(snapshot_get_item(snap, i)) == key) + return i; + } + return -1; +} + + + +typedef struct +{ + int num_deleted_items; + int num_update_items; + int num_temp_items; /* needed? */ + int data[1]; + + /* + char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; } + + int deleted_item(int index) { return offsets[index]; } + item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); } + item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); } + */ +} SNAPSHOT_DELTA; + + +static const int MAX_ITEMS = 512; +static SNAPSHOT_DELTA empty = {0,0,0,{0}}; + +void *snapshot_empty_delta() +{ + return ∅ +} + +int snapshot_crc(SNAPSHOT *snap) +{ + int crc = 0; + int i, b; + SNAPSHOT_ITEM *item; + int size; + + for(i = 0; i < snap->num_items; i++) + { + item = snapshot_get_item(snap, i); + size = snapshot_get_item_datasize(snap, i); + + for(b = 0; b < size/4; b++) + crc += snapitem_data(item)[b]; + } + return crc; +} + +void snapshot_debug_dump(SNAPSHOT *snap) +{ + int size, i, b; + SNAPSHOT_ITEM *item; + + dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items); + for(i = 0; i < snap->num_items; i++) + { + item = snapshot_get_item(snap, i); + size = snapshot_get_item_datasize(snap, i); + dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item)); + for(b = 0; b < size/4; b++) + dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]); + } +} + +static int diff_item(int *past, int *current, int *out, int size) +{ + int needed = 0; + while(size) + { + *out = *current-*past; + needed |= *out; + out++; + past++; + current++; + size--; + } + + return needed; +} + +int snapshot_data_rate[0xffff] = {0}; +int snapshot_data_updates[0xffff] = {0}; +static int snapshot_current = 0; + +static void undiff_item(int *past, int *diff, int *out, int size) +{ + while(size) + { + *out = *past+*diff; + + if(*diff == 0) + snapshot_data_rate[snapshot_current] += 1; + else + { + unsigned char buf[16]; + unsigned char *end = vint_pack(buf, *diff); + snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; + } + + out++; + past++; + diff++; + size--; + } +} + + +/* TODO: OPT: this should be made much faster */ +int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata) +{ + SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata; + int *data = (int *)delta->data; + int i, itemsize, pastindex; + SNAPSHOT_ITEM *fromitem; + SNAPSHOT_ITEM *curitem; + SNAPSHOT_ITEM *pastitem; + int count = 0; + int size_count = 0; + + delta->num_deleted_items = 0; + delta->num_update_items = 0; + delta->num_temp_items = 0; + + /* pack deleted stuff */ + for(i = 0; i < from->num_items; i++) + { + fromitem = snapshot_get_item(from, i); + if(snapshot_get_item_index(to, (snapitem_key(fromitem))) == -1) + { + /* deleted */ + delta->num_deleted_items++; + *data = snapitem_key(fromitem); + data++; + } + } + + /* pack updated stuff */ + for(i = 0; i < to->num_items; i++) + { + /* do delta */ + itemsize = snapshot_get_item_datasize(to, i); + + curitem = snapshot_get_item(to, i); + pastindex = snapshot_get_item_index(from, snapitem_key(curitem)); + if(pastindex != -1) + { + pastitem = snapshot_get_item(from, pastindex); + if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), data+3, itemsize/4)) + { + *data++ = itemsize; + *data++ = snapitem_type(curitem); + *data++ = snapitem_id(curitem); + /*data++ = curitem->key();*/ + data += itemsize/4; + delta->num_update_items++; + } + } + else + { + *data++ = itemsize; + *data++ = snapitem_type(curitem); + *data++ = snapitem_id(curitem); + /*data++ = curitem->key();*/ + + mem_copy(data, snapitem_data(curitem), itemsize); + size_count += itemsize; + data += itemsize/4; + delta->num_update_items++; + count++; + } + } + + if(0) + { + dbg_msg("snapshot", "%d %d %d", + delta->num_deleted_items, + delta->num_update_items, + delta->num_temp_items); + } + + /* + // TODO: pack temp stuff + + // finish + //mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int)); + //mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int)); + //mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int)); + //mem_copy(delta->data_start(), data, data_size); + //delta->data_size = data_size; + * */ + + if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items) + return 0; + + return (int)((char*)data-(char*)dstdata); +} + +int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size) +{ + SNAPBUILD builder; + SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata; + int *data = (int *)delta->data; + SNAPSHOT_ITEM *fromitem; + int i, d, keep, itemsize; + int *deleted; + int id, type, key; + int fromindex; + int *newdata; + + snapbuild_init(&builder); + + /* unpack deleted stuff */ + deleted = data; + data += delta->num_deleted_items; + + /* copy all non deleted stuff */ + for(i = 0; i < from->num_items; i++) + { + /* dbg_assert(0, "fail!"); */ + fromitem = snapshot_get_item(from, i); + itemsize = snapshot_get_item_datasize(from, i); + keep = 1; + for(d = 0; d < delta->num_deleted_items; d++) + { + if(deleted[d] == snapitem_key(fromitem)) + { + keep = 0; + break; + } + } + + if(keep) + { + /* keep it */ + mem_copy( + snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize), + snapitem_data(fromitem), itemsize); + } + } + + /* unpack updated stuff */ + for(i = 0; i < delta->num_update_items; i++) + { + itemsize = *data++; + type = *data++; + id = *data++; + snapshot_current = type; + + key = (type<<16)|id; + + /* create the item if needed */ + newdata = snapbuild_get_item_data(&builder, key); + if(!newdata) + newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize); + + fromindex = snapshot_get_item_index(from, key); + if(fromindex != -1) + { + /* we got an update so we need to apply the diff */ + undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4); + snapshot_data_updates[snapshot_current]++; + } + else /* no previous, just copy the data */ + { + mem_copy(newdata, data, itemsize); + snapshot_data_rate[snapshot_current] += itemsize*8; + snapshot_data_updates[snapshot_current]++; + } + + data += itemsize/4; + } + + /* finish up */ + return snapbuild_finish(&builder, to); +} + +/* SNAPSTORAGE */ + +void snapstorage_init(SNAPSTORAGE *ss) +{ + ss->first = 0; +} + +void snapstorage_purge_all(SNAPSTORAGE *ss) +{ + SNAPSTORAGE_HOLDER *h = ss->first; + SNAPSTORAGE_HOLDER *next; + + while(h) + { + next = h->next; + mem_free(h); + h = next; + } + + /* no more snapshots in storage */ + ss->first = 0; + ss->last = 0; +} + +void snapstorage_purge_until(SNAPSTORAGE *ss, int tick) +{ + SNAPSTORAGE_HOLDER *next; + SNAPSTORAGE_HOLDER *h = ss->first; + + while(h) + { + next = h->next; + if(h->tick >= tick) + return; /* no more to remove */ + mem_free(h); + + ss->first = next; + next->prev = 0x0; + + h = next; + } + + /* no more snapshots in storage */ + ss->first = 0; + ss->last = 0; +} + +void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data) +{ + /* allocate memory for holder + snapshot_data */ + SNAPSTORAGE_HOLDER *h = (SNAPSTORAGE_HOLDER *)mem_alloc(sizeof(SNAPSTORAGE_HOLDER)+data_size, 1); + + /* set data */ + h->tick = tick; + h->tagtime = tagtime; + h->snap_size = data_size; + h->snap = (SNAPSHOT*)(h+1); + mem_copy(h->snap, data, data_size); + + /* link */ + h->next = 0; + h->prev = ss->last; + if(ss->last) + ss->last->next = h; + else + ss->first = h; + ss->last = h; +} + +int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data) +{ + SNAPSTORAGE_HOLDER *h = ss->first; + + while(h) + { + if(h->tick == tick) + { + if(tagtime) + *tagtime = h->tagtime; + if(data) + *data = h->snap; + return h->snap_size; + } + + h = h->next; + } + + return -1; +} + +/* SNAPBUILD */ + +void snapbuild_init(SNAPBUILD *sb) +{ + sb->data_size = 0; + sb->num_items = 0; +} + +SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index) +{ + return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]); +} + +int *snapbuild_get_item_data(SNAPBUILD *sb, int key) +{ + int i; + for(i = 0; i < sb->num_items; i++) + { + if(snapitem_key(snapbuild_get_item(sb, i)) == key) + return (int *)snapitem_data(snapbuild_get_item(sb, i)); + } + return 0; +} + +int snapbuild_finish(SNAPBUILD *sb, void *snapdata) +{ + /* flattern and make the snapshot */ + SNAPSHOT *snap = (SNAPSHOT *)snapdata; + int offset_size = sizeof(int)*sb->num_items; + snap->data_size = sb->data_size; + snap->num_items = sb->num_items; + mem_copy(snapshot_offsets(snap), sb->offsets, offset_size); + mem_copy(snapshot_datastart(snap), sb->data, sb->data_size); + return sizeof(SNAPSHOT) + offset_size + sb->data_size; +} + +void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size) +{ + SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size); + mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size); + obj->type_and_id = (type<<16)|id; + sb->offsets[sb->num_items] = sb->data_size; + sb->data_size += sizeof(SNAPSHOT_ITEM) + size; + sb->num_items++; + + dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data"); + dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items"); + + return snapitem_data(obj); +} diff --git a/src/engine/e_snapshot.h b/src/engine/e_snapshot.h new file mode 100644 index 00000000..0a74b0dc --- /dev/null +++ b/src/engine/e_snapshot.h @@ -0,0 +1,91 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef ENGINE_SNAPSHOT_H +#define ENGINE_SNAPSHOT_H + +#include "e_system.h" + +/* SNAPSHOT */ + +enum +{ + MAX_SNAPSHOT_SIZE=64*1024 +}; + +typedef struct +{ + int type_and_id; +} SNAPSHOT_ITEM; + +typedef struct +{ + int data_size; + int num_items; +} SNAPSHOT; + +int *snapitem_data(SNAPSHOT_ITEM *item); +int snapitem_type(SNAPSHOT_ITEM *item); +int snapitem_id(SNAPSHOT_ITEM *item); +int snapitem_key(SNAPSHOT_ITEM *item); + +int *snapshot_offsets(SNAPSHOT *snap); +char *snapshot_datastart(SNAPSHOT *snap); + +SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index); +int snapshot_get_item_datasize(SNAPSHOT *snap, int index); +int snapshot_get_item_index(SNAPSHOT *snap, int key); + +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); + +/* SNAPSTORAGE */ + +typedef struct SNAPSTORAGE_HOLDER_t +{ + struct SNAPSTORAGE_HOLDER_t *prev; + struct SNAPSTORAGE_HOLDER_t *next; + + int64 tagtime; + int tick; + + int snap_size; + SNAPSHOT *snap; +} SNAPSTORAGE_HOLDER; + +typedef struct SNAPSTORAGE_t +{ + SNAPSTORAGE_HOLDER *first; + SNAPSTORAGE_HOLDER *last; +} SNAPSTORAGE; + +void snapstorage_init(SNAPSTORAGE *ss); +void snapstorage_purge_all(SNAPSTORAGE *ss); +void snapstorage_purge_until(SNAPSTORAGE *ss, int tick); +void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data); +int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data); + +/* SNAPBUILD */ + +enum +{ + SNAPBUILD_MAX_ITEMS = 512 +}; + +typedef struct SNAPBUILD +{ + char data[MAX_SNAPSHOT_SIZE]; + int data_size; + + int offsets[SNAPBUILD_MAX_ITEMS]; + int num_items; +} SNAPBUILD; + +void snapbuild_init(SNAPBUILD *sb); +SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index); +int *snapbuild_get_item_data(SNAPBUILD *sb, int key); +int snapbuild_finish(SNAPBUILD *sb, void *snapdata); +void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size); + +#endif /* ENGINE_SNAPSHOT_H */ diff --git a/src/engine/e_system.c b/src/engine/e_system.c new file mode 100644 index 00000000..90aeccc5 --- /dev/null +++ b/src/engine/e_system.c @@ -0,0 +1,723 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include +#include +#include +#include + +#include "e_detect.h" +#include "e_system.h" + +#if defined(CONF_FAMILY_UNIX) + #include + #include + + /* unix net includes */ + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include +#elif defined(CONF_FAMILY_WINDOWS) + #define WIN32_LEAN_AND_MEAN + #define _WIN32_WINNT 0x0400 + #include + #include + #include + #include /* for SHGetFolderPathAndSubDir */ + #include + #include + #include + + #define EWOULDBLOCK WSAEWOULDBLOCK +#else + #error NOT IMPLEMENTED +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +IOHANDLE logfile = 0; + +void dbg_assert_imp(const char *filename, int line, int test, const char *msg) +{ + if(!test) + { + dbg_msg("assert", "%s(%d): %s", filename, line, msg); + dbg_break(); + } +} + +void dbg_break() +{ + *((unsigned*)0) = 0x0; +} + +int dbg_log_to_file(const char *filename) +{ + logfile = io_open(filename, IOFLAG_WRITE); + if(logfile) + return 1; + return 0; +} + +void dbg_msg(const char *sys, const char *fmt, ...) +{ + va_list args; +#if defined(CONF_FAMILY_WINDOWS) + char str[1024]; + va_start(args, fmt); + vsprintf(str, fmt, args); + va_end(args); + OutputDebugString(str); + OutputDebugString("\n"); +#endif + + va_start(args, fmt); + printf("[%08x][%s]: ", (int)time(0), sys); + vprintf(fmt, args); + va_end(args); + printf("\n"); + fflush(stdout); +} + +int memory_alloced = 0; + +struct memheader +{ + const char *filename; + int line; + int size; + struct memheader *prev; + struct memheader *next; +}; + +struct memtail +{ + int guard; +}; + +struct memheader *first = 0; + +int mem_allocated() +{ + return memory_alloced; +} + +void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment) +{ + /* TODO: fix alignment */ + /* TODO: add debugging */ + struct memheader *header = (struct memheader *)malloc(size+sizeof(struct memheader)+sizeof(struct memtail)); + struct memtail *tail = (struct memtail *)(((char*)(header+1))+size); + header->size = size; + header->filename = filename; + header->line = line; + memory_alloced += header->size; + + tail->guard = 0xbaadc0de; + + header->prev = (struct memheader *)0; + header->next = first; + if(first) + first->prev = header; + first = header; + + /*dbg_msg("mem", "++ %p", header+1); */ + return header+1; +} + +void mem_free(void *p) +{ + if(p) + { + struct memheader *header = (struct memheader *)p - 1; + struct memtail *tail = (struct memtail *)(((char*)(header+1))+header->size); + + if(tail->guard != 0xbaadc0de) + dbg_msg("mem", "!! %p", p); + /* dbg_msg("mem", "-- %p", p); */ + memory_alloced -= header->size; + + if(header->prev) + header->prev->next = header->next; + else + first = header->next; + if(header->next) + header->next->prev = header->prev; + + free(header); + } +} + +void mem_debug_dump() +{ + char buf[1024]; + struct memheader *header = first; + IOHANDLE f = io_open("memory.txt", IOFLAG_WRITE); + + while(header) + { + sprintf(buf, "%s(%d): %d\n", header->filename, header->line, header->size); + io_write(f, buf, strlen(buf)); + header = header->next; + } +} + + +void mem_copy(void *dest, const void *source, unsigned size) +{ + memcpy(dest, source, size); +} + +void mem_move(void *dest, const void *source, unsigned size) +{ + memmove(dest, source, size); +} + +void mem_zero(void *block,unsigned size) +{ + memset(block, 0, size); +} + +IOHANDLE io_open(const char *filename, int flags) +{ + if(flags == IOFLAG_READ) + return (IOHANDLE)fopen(filename, "rb"); + if(flags == IOFLAG_WRITE) + return (IOHANDLE)fopen(filename, "wb"); + return 0x0; +} + +unsigned io_read(IOHANDLE io, void *buffer, unsigned size) +{ + return fread(buffer, 1, size, (FILE*)io); +} + +unsigned io_skip(IOHANDLE io, unsigned size) +{ + fseek((FILE*)io, size, SEEK_CUR); + return size; +} + +int io_seek(IOHANDLE io, int offset, int origin) +{ + int real_origin; + + switch(origin) + { + case IOSEEK_START: + real_origin = SEEK_SET; + break; + case IOSEEK_CUR: + real_origin = SEEK_CUR; + break; + case IOSEEK_END: + real_origin = SEEK_END; + } + + return fseek((FILE*)io, offset, origin); +} + +long int io_tell(IOHANDLE io) +{ + return ftell((FILE*)io); +} + +long int io_length(IOHANDLE io) +{ + long int length; + io_seek(io, 0, IOSEEK_END); + length = io_tell(io); + io_seek(io, 0, IOSEEK_START); + return length; +} + +unsigned io_write(IOHANDLE io, const void *buffer, unsigned size) +{ + return fwrite(buffer, 1, size, (FILE*)io); +} + +int io_close(IOHANDLE io) +{ + fclose((FILE*)io); + return 1; +} + +void thread_sleep(int milliseconds) +{ +#if defined(CONF_FAMILY_UNIX) + usleep(milliseconds*1000); +#elif defined(CONF_FAMILY_WINDOWS) + Sleep(milliseconds); +#else + #error not implemented +#endif +} + +#if defined(CONF_FAMILY_UNIX) +typedef pthread_mutex_t LOCKINTERNAL; +#elif defined(CONF_FAMILY_WINDOWS) +typedef CRITICAL_SECTION LOCKINTERNAL; +#else + #error not implemented on this platform +#endif + +LOCK lock_create() +{ + LOCKINTERNAL *lock = (LOCKINTERNAL*)mem_alloc(sizeof(LOCKINTERNAL), 4); + +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_init(lock, 0x0); +#elif defined(CONF_FAMILY_WINDOWS) + InitializeCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif + return (LOCK)lock; +} + +void lock_destroy(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_destroy((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + DeleteCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif + mem_free(lock); +} + +int lock_try(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + return pthread_mutex_trylock((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + return TryEnterCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif +} + +void lock_wait(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_lock((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + EnterCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif +} + +void lock_release(LOCK lock) +{ +#if defined(CONF_FAMILY_UNIX) + pthread_mutex_unlock((LOCKINTERNAL *)lock); +#elif defined(CONF_FAMILY_WINDOWS) + LeaveCriticalSection((LPCRITICAL_SECTION)lock); +#else + #error not implemented on this platform +#endif +} + +/* ----- time ----- */ +int64 time_get() +{ +#if defined(CONF_FAMILY_UNIX) + struct timeval val; + gettimeofday(&val, NULL); + return (int64)val.tv_sec*(int64)1000000+(int64)val.tv_usec; +#elif defined(CONF_FAMILY_WINDOWS) + static int64 last = 0; + int64 t; + QueryPerformanceCounter((PLARGE_INTEGER)&t); + if(tsin_family = AF_INET; + p->sin_port = htons(src->port); + p->sin_addr.s_addr = htonl(src->ip[0]<<24|src->ip[1]<<16|src->ip[2]<<8|src->ip[3]); +} + +static void sockaddr_to_netaddr4(const struct sockaddr *src, NETADDR4 *dst) +{ + unsigned int ip = htonl(((struct sockaddr_in*)src)->sin_addr.s_addr); + dst->port = htons(((struct sockaddr_in*)src)->sin_port); + dst->ip[0] = (unsigned char)((ip>>24)&0xFF); + dst->ip[1] = (unsigned char)((ip>>16)&0xFF); + dst->ip[2] = (unsigned char)((ip>>8)&0xFF); + dst->ip[3] = (unsigned char)(ip&0xFF); +} + +int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b) +{ + if( a->ip[0] != b->ip[0] || + a->ip[1] != b->ip[1] || + a->ip[2] != b->ip[2] || + a->ip[3] != b->ip[3] || + a->port != b->port + ) + return 1; + return 0; +} + + +int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr) +{ + struct hostent* ip = gethostbyname(hostname); + + if(ip && ip->h_length > 0) + { + addr->ip[0] = ip->h_addr_list[0][0]; + addr->ip[1] = ip->h_addr_list[0][1]; + addr->ip[2] = ip->h_addr_list[0][2]; + addr->ip[3] = ip->h_addr_list[0][3]; + addr->port = port; + return 0; + } + + return -1; +} + +NETSOCKET net_udp4_create(NETADDR4 bindaddr) +{ + struct sockaddr addr; + unsigned int mode = 1; + int broadcast = 1; + + /* create socket */ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if(sock < 0) + return NETSOCKET_INVALID; + + /* bind, we should check for error */ + netaddr4_to_sockaddr(&bindaddr, &addr); + if(bind(sock, &addr, sizeof(addr)) != 0) + { + net_udp4_close(sock); + return NETSOCKET_INVALID; + } + + /* set non-blocking */ +#if defined(CONF_FAMILY_WINDOWS) + ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); +#else + ioctl(sock, FIONBIO, (unsigned long *)&mode); +#endif + + /* set boardcast */ + setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); + + /* return */ + return sock; +} + +int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size) +{ + struct sockaddr sa; + int d; + mem_zero(&sa, sizeof(sa)); + netaddr4_to_sockaddr(addr, &sa); + d = sendto((int)sock, (const char*)data, size, 0, &sa, sizeof(sa)); + if(d < 0) + dbg_msg("net", "sendto error %d %x", d, d); + return d; +} + +int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize) +{ + struct sockaddr from; + int bytes; + socklen_t fromlen = sizeof(struct sockaddr); + bytes = recvfrom(sock, (char*)data, maxsize, 0, &from, &fromlen); + if(bytes > 0) + { + sockaddr_to_netaddr4(&from, addr); + return bytes; + } + else if(bytes == 0) + return 0; + return -1; /* error */ +} + +int net_udp4_close(NETSOCKET sock) +{ +#if defined(CONF_FAMILY_WINDOWS) + closesocket(sock); +#else + close((int)sock); +#endif + return 0; +} + +NETSOCKET net_tcp4_create(const NETADDR4 *a) +{ + struct sockaddr addr; + + /* create socket */ + int sock = socket(AF_INET, SOCK_STREAM, 0); + if(sock < 0) + return NETSOCKET_INVALID; + + /* bind, we should check for error */ + netaddr4_to_sockaddr(a, &addr); + bind(sock, &addr, sizeof(addr)); + + /* return */ + return sock; +} + +int net_tcp4_set_non_blocking(NETSOCKET sock) +{ + unsigned int mode = 1; +#if defined(CONF_FAMILY_WINDOWS) + return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); +#else + return ioctl(sock, FIONBIO, (unsigned long *)&mode); +#endif +} + +int net_tcp4_set_blocking(NETSOCKET sock) +{ + unsigned int mode = 0; +#if defined(CONF_FAMILY_WINDOWS) + return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); +#else + return ioctl(sock, FIONBIO, (unsigned long *)&mode); +#endif +} + +int net_tcp4_listen(NETSOCKET sock, int backlog) +{ + return listen(sock, backlog); +} + +int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a) +{ + int s; + socklen_t sockaddr_len; + struct sockaddr addr; + + sockaddr_len = sizeof(addr); + + s = accept(sock, &addr, &sockaddr_len); + + if (s != -1) + { + sockaddr_to_netaddr4(&addr, a); + *new_sock = s; + } + return s; +} + +int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a) +{ + struct sockaddr addr; + + netaddr4_to_sockaddr(a, &addr); + return connect(sock, &addr, sizeof(addr)); +} + +int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a) +{ + struct sockaddr addr; + int res; + + netaddr4_to_sockaddr(a, &addr); + net_tcp4_set_non_blocking(sock); + res = connect(sock, &addr, sizeof(addr)); + net_tcp4_set_blocking(sock); + + return res; +} + +int net_tcp4_send(NETSOCKET sock, const void *data, int size) +{ + int d; + d = send((int)sock, (const char*)data, size, 0); + return d; +} + +int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize) +{ + int bytes; + bytes = recv((int)sock, (char*)data, maxsize, 0); + return bytes; +} + +int net_tcp4_close(NETSOCKET sock) +{ +#if defined(CONF_FAMILY_WINDOWS) + closesocket(sock); +#else + close((int)sock); +#endif + return 0; +} + +int net_errno() +{ + return errno; +} + +int net_would_block() +{ + return net_errno() == EWOULDBLOCK; +} + +int net_init() +{ +#if defined(CONF_FAMILY_WINDOWS) + WSADATA wsaData; + int err = WSAStartup(MAKEWORD(1, 1), &wsaData); + dbg_assert(err == 0, "network initialization failed."); + return err==0?0:1; +#endif + + return 0; +} + +int fs_listdir(const char *dir, fs_listdir_callback cb, void *user) +{ +#if defined(CONF_FAMILY_WINDOWS) + WIN32_FIND_DATA finddata; + HANDLE handle; + char buffer[1024*2]; + strcpy(buffer, dir); + strcat(buffer, "/*"); + + handle = FindFirstFileA(buffer, &finddata); + + if (handle == INVALID_HANDLE_VALUE) + return 0; + + /* add all the entries */ + do + { + if(finddata.cFileName[0] != '.') + cb(finddata.cFileName, 0, user); + } while (FindNextFileA(handle, &finddata)); + + FindClose(handle); + return 0; +#else + struct dirent *entry; + DIR *d = opendir(dir); + + if(!d) + return 0; + + while((entry = readdir(d)) != NULL) + cb(entry->d_name, 0, user); + + /* close the directory and return */ + closedir(d); + return 0; +#endif +} + +int fs_storage_path(const char *appname, char *path, int max) +{ +#if defined(CONF_FAMILY_WINDOWS) + HRESULT r; + char home[MAX_PATH]; + r = SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, home); + if(r != 0) + return 1; + _snprintf(path, max, "%s/%s", home, appname); + return 0; +#else + char *home = getenv("HOME"); + int i; + if(!home) + return 0; + + snprintf(path, max, "%s/.%s", home, appname); + for(i = strlen(home)+2; path[i]; i++) + path[i] = tolower(path[i]); + + return 1; +#endif +} + +int fs_makedir(const char *path) +{ +#if defined(CONF_FAMILY_WINDOWS) + if(_mkdir(path) == 0) + return 0; + if(errno == EEXIST) + return 0; + return 1; +#else + if(mkdir(path, 0755) == 0) + return 0; + if(errno == EEXIST) + return 0; + return 1; +#endif +} + +void swap_endian(void *data, unsigned elem_size, unsigned num) +{ + char *src = (char*) data; + char *dst = src + (elem_size - 1); + + while(num) + { + unsigned n = elem_size>>1; + char tmp; + while(n) + { + tmp = *src; + *src = *dst; + *dst = tmp; + + src++; + dst--; + n--; + } + + src = src + (elem_size>>1); + dst = src + (elem_size - 1); + num--; + } +} + +#if defined(__cplusplus) +} +#endif diff --git a/src/engine/e_system.h b/src/engine/e_system.h new file mode 100644 index 00000000..4802111f --- /dev/null +++ b/src/engine/e_system.h @@ -0,0 +1,531 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#ifndef BASE_SYSTEM_H +#define BASE_SYSTEM_H + +#include "e_detect.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Group: Debug */ + +/********** + Function: dbg_assert + + Breaks into the debugger based on a test. + + Parameters: + test - Result of the test. + msg - Message that should be printed if the test fails. + + Remarks: + Does nothing in release version of the library. + + See Also: + +**********/ +void dbg_assert(int test, const char *msg); +#define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) +void dbg_assert_imp(const char *filename, int line, int test, const char *msg); + +/********** + Function: dbg_break + + Breaks into the debugger. + + Remarks: + Does nothing in release version of the library. + + See Also: + +**********/ +void dbg_break(); + +/********** + Function: dbg_msg + + Prints a debug message. + + Parameters: + sys - A string that describes what system the message belongs to + fmt - A printf styled format string. + + Remarks: + Does nothing in relase version of the library. + + See Also: + +**********/ +void dbg_msg(const char *sys, const char *fmt, ...); + +/* Group: Memory */ + +/********** + Function: mem_alloc + + Allocates memory. + + Parameters: + size - Size of the needed block. + alignment - Alignment for the block. + + Returns: + Returns a pointer to the newly allocated block. Returns a + null pointer if the memory couldn't be allocated. + + Remarks: + - Passing 0 to size will allocated the smallest amount possible + and return a unique pointer. + + See Also: + +**********/ +void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment); +#define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a)) + +/********** + Function: mem_free + + Frees a block allocated through . + + Remarks: + - In the debug version of the library the function will assert if + a non-valid block is passed, like a null pointer or a block that + isn't allocated. + + See Also: + +**********/ +void mem_free(void *block); + +/********** + Function: mem_copy + Copies a a memory block. + + Parameters: + dest - Destination. + source - Source to copy. + size - Size of the block to copy. + + Remarks: + - This functions DOES NOT handles cases where source and + destination is overlapping. + + See Also: + +**********/ +void mem_copy(void *dest, const void *source, unsigned size); + +/********** + Function: mem_move + Copies a a memory block. + + Parameters: + dest - Destination. + source - Source to copy. + size - Size of the block to copy. + + Remarks: + - This functions handles cases where source and destination is overlapping. + + See Also: + +**********/ +void mem_move(void *dest, const void *source, unsigned size); + +/********** + Function: mem_zero + Sets a complete memory block to 0. + + Parameters: + block - Pointer to the block to zero out. + size - Size of the block. +**********/ +void mem_zero(void *block, unsigned size); + +/* ------- io ------- */ +enum { + IOFLAG_READ = 1, + IOFLAG_WRITE = 2, + IOFLAG_RANDOM = 4, + + IOSEEK_START = 0, + IOSEEK_CUR = 1, + IOSEEK_END = 2 +}; + +typedef struct IOINTERNAL *IOHANDLE; + +/**** Group: File IO ****/ + +/**** + Function: io_open + + Opens a file. + + Parameters: + filename - File to open. + flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM. + + Returns: + Returns a handle to the file on success and 0 on failure. + +****/ +IOHANDLE io_open(const char *filename, int flags); + +/**** + Function: io_read + + Reads data into a buffer from a file. + + Parameters: + io - Handle to the file to read data from. + buffer - Pointer to the buffer that will recive the data. + size - Number of bytes to read from the file. + + Returns: + Number of bytes read. + +****/ +unsigned io_read(IOHANDLE io, void *buffer, unsigned size); + +/***** + Function: io_skip + + Skips data in a file. + + Parameters: + io - Handle to the file. + size - Number of bytes to skip. + + Returns: + Number of bytes skipped. +****/ +unsigned io_skip(IOHANDLE io, unsigned size); + +/***** + Function: io_write + + Writes data from a buffer to file. + + Parameters: + io - Handle to the file. + buffer - Pointer to the data that should be written. + size - Number of bytes to write. + + Returns: + Number of bytes written. +*****/ +unsigned io_write(IOHANDLE io, const void *buffer, unsigned size); + +/***** + Function: io_seek + + Seeks to a specified offset in the file. + + Parameters: + io - Handle to the file. + offset - Offset from pos to stop. + origin - Position to start searching from. + + Returns: + Returns 0 on success. +*****/ +int io_seek(IOHANDLE io, int offset, int origin); + +/***** + Function: io_tell + + Gets the current position in the file. + + Parameters: + io - Handle to the file. + + Returns: + Returns the current position. -1L if an error occured. +*****/ +long int io_tell(IOHANDLE io); + +/***** + Function: io_length + + Gets the total length of the file. Resetting cursor to the beginning + + Parameters: + io - Handle to the file. + + Returns: + Returns the total size. -1L if an error occured. +*****/ +long int io_length(IOHANDLE io); + +/***** + Function: io_close + + Closes a file. + + Parameters: + io - Handle to the file. + + Returns: + Returns 0 on success. +*****/ +int io_close(IOHANDLE io); + +/**** Group: Threads ****/ + +int thread_create(); /* NOT IMPLEMENTED */ +int thread_destory(); /* NOT IMPLEMENTED */ + +int thread_run(); /* NOT IMPLEMENTED */ +int thread_pause(); /* NOT IMPLEMENTED */ +int thread_wait(); /* NOT IMPLEMENTED */ + +/***** + Function: thread_sleep + + Suspends the current thread for a given period. + + Parameters: + milliseconds - Number of milliseconds to sleep. +*****/ +void thread_sleep(int milliseconds); + +/**** Group: Locks ****/ +typedef void* LOCK; + +LOCK lock_create(); +void lock_destroy(LOCK lock); + +int lock_try(LOCK lock); +void lock_wait(LOCK lock); +void lock_release(LOCK lock); + +/**** Group: Timer ****/ +#ifdef __GNUC__ +/* if compiled with -pedantic-errors it will complain about long + not being a C90 thing. +*/ +__extension__ typedef long long int64; +#else +typedef long long int64; +#endif +/***** + Function: time_get + + Fetches a sample from a high resolution timer. + + Returns: + Current value of the timer. + + Remarks: + To know how fast the timer is ticking, see . +*****/ +int64 time_get(); + +/***** + Function: time_freq + + Returns the frequency of the high resolution timer. + + Returns: + Returns the frequency of the high resolution timer. +*****/ +int64 time_freq(); + +/**** Group: Network General ipv4 ****/ +typedef int NETSOCKET; +enum +{ + NETSOCKET_INVALID = -1 +}; + +typedef struct +{ + unsigned char ip[4]; + unsigned short port; +} NETADDR4; + +/***** + Function: net_host_lookup + + Does a hostname lookup by name and fills out the passed NETADDE4 struct with the recieved details. + + Returns: + 0 on success. +*****/ +int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr); + +/**** Group: Network UDP4 ****/ + +/***** + Function: net_udp4_create + + Creates a UDP4 socket and binds it to a port. + + Parameters: + port - Port to bind the socket to. + + Returns: + On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. +*****/ +NETSOCKET net_udp4_create(NETADDR4 bindaddr); + +/***** + Function: net_udp4_send + + Sends a packet over an UDP4 socket. + + Parameters: + sock - Socket to use. + addr - Where to send the packet. + data - Pointer to the packet data to send. + size - Size of the packet. + + Returns: + On success it returns the number of bytes sent. Returns -1 on error. +*****/ +int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size); + +/***** + Function: net_udp4_recv + + Recives a packet over an UDP4 socket. + + Parameters: + sock - Socket to use. + addr - Pointer to an NETADDR4 that will recive the address. + data - Pointer to a buffer that will recive the data. + maxsize - Maximum size to recive. + + Returns: + On success it returns the number of bytes recived. Returns -1 on error. +*****/ +int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize); + +/***** + Function: net_udp4_close + + Closes an UDP4 socket. + + Parameters: + sock - Socket to close. + + Returns: + Returns 0 on success. -1 on error. +*****/ +int net_udp4_close(NETSOCKET sock); + + +/**** Group: Network TCP4 ****/ + +/***** + Function: net_tcp4_create + + DOCTODO: serp +*****/ +NETSOCKET net_tcp4_create(const NETADDR4 *a); + +/***** + Function: net_tcp4_set_non_blocking + + DOCTODO: serp +*****/ +int net_tcp4_set_non_blocking(NETSOCKET sock); + +/***** + Function: net_tcp4_set_non_blocking + + DOCTODO: serp +*****/ +int net_tcp4_set_blocking(NETSOCKET sock); + +/***** + Function: net_tcp4_listen + + DOCTODO: serp +*****/ +int net_tcp4_listen(NETSOCKET sock, int backlog); + +/***** + Function: net_tcp4_accept + + DOCTODO: serp +*****/ +int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a); + +/***** + Function: net_tcp4_connect + + DOCTODO: serp +*****/ +int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a); + +/***** + Function: net_tcp4_connect_non_blocking + + DOCTODO: serp +*****/ +int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a); + +/***** + Function: net_tcp4_send + + DOCTODO: serp +*****/ +int net_tcp4_send(NETSOCKET sock, const void *data, int size); + +/***** + Function: net_tcp4_recv + + DOCTODO: serp +*****/ +int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize); + +/***** + Function: net_tcp4_close + + DOCTODO: serp +*****/ +int net_tcp4_close(NETSOCKET sock); + +/***** + Function: net_errno + + DOCTODO: serp +*****/ +int net_errno(); + +/***** + Function: net_would_block + + DOCTODO: serp +*****/ +int net_would_block(); + +/***** + Function: net_init + + DOCTODO: serp +*****/ +int net_init(); + + + +/* NOT DOCUMENTED */ +typedef void (*fs_listdir_callback)(const char *name, int is_dir, void *user); +int fs_listdir(const char *dir, fs_listdir_callback cb, void *user); +int fs_storage_path(const char *appname, char *path, int max); +int fs_makedir(const char *path); +int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b); + +void mem_debug_dump(); +int mem_allocated(); + +void swap_endian(void *data, unsigned elem_size, unsigned num); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/engine/engine.c b/src/engine/engine.c deleted file mode 100644 index 08f13184..00000000 --- a/src/engine/engine.c +++ /dev/null @@ -1,66 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include -#include -#include - -static char application_save_path[512] = {0}; - -const char *engine_savepath(const char *filename, char *buffer, int max) -{ - sprintf(buffer, "%s/%s", application_save_path, filename); - return buffer; -} - -void engine_init(const char *appname, int argc, char **argv) -{ - /* init the network */ - net_init(); - - /* create storage location */ - { - char path[1024] = {0}; - fs_storage_path(appname, application_save_path, sizeof(application_save_path)); - if(fs_makedir(application_save_path) == 0) - { - strcpy(path, application_save_path); - strcat(path, "/screenshots"); - fs_makedir(path); - } - } - - /* reset the config */ - config_reset(); - - /* load the configuration */ - { - int i; - const char *config_filename = "default.cfg"; - char buf[1024]; - for(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(engine_savepath(config_filename, buf, sizeof(buf))); - } - - /* search arguments for overrides */ - { - int i; - for(i = 1; i < argc; i++) - config_set(argv[i]); - } -} - -void engine_writeconfig() -{ - char buf[1024]; - config_save(engine_savepath("default.cfg", buf, sizeof(buf))); -} diff --git a/src/engine/engine.h b/src/engine/engine.h deleted file mode 100644 index 56fec20e..00000000 --- a/src/engine/engine.h +++ /dev/null @@ -1,5 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -const char *engine_savepath(const char *filename, char *buffer, int max); -void engine_init(const char *appname, int argc, char **argv); -void engine_writeconfig(); diff --git a/src/engine/external/pa.c b/src/engine/external/pa.c index a417e9db..71afc065 100644 --- a/src/engine/external/pa.c +++ b/src/engine/external/pa.c @@ -1,5 +1,5 @@ /* Include the auto configuration */ -#include +#include /* common part */ #include "portaudio/pa_converters.c" diff --git a/src/engine/interface.h b/src/engine/interface.h deleted file mode 100644 index 5cd8d604..00000000 --- a/src/engine/interface.h +++ /dev/null @@ -1,858 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_INTERFACE_H -#define ENGINE_INTERFACE_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* - Title: Engine Interface -*/ - -#include "keys.h" - -enum -{ - MAX_CLIENTS=12, - SERVER_TICK_SPEED=50, /* TODO: this should be removed */ - SNAP_CURRENT=0, - SNAP_PREV=1, - - IMG_RGB=0, - IMG_RGBA=1, - - MASK_NONE=0, - MASK_SET, - MASK_ZERO, - - SNDFLAG_LOOP=1, - SNDFLAG_POS=2, - SNDFLAG_ALL=3, - - CLIENTSTATE_OFFLINE=0, - CLIENTSTATE_CONNECTING, - CLIENTSTATE_LOADING, - CLIENTSTATE_ONLINE, - CLIENTSTATE_QUITING, - - BROWSESORT_NAME = 0, - BROWSESORT_PING, - BROWSESORT_MAP, - BROWSESORT_GAMETYPE, - BROWSESORT_PROGRESSION, - BROWSESORT_NUMPLAYERS -}; - -typedef struct -{ - int type; - int id; -} SNAP_ITEM; - -typedef struct -{ - const char *name; - int latency; -} CLIENT_INFO; - -typedef struct -{ - int width, height; - int format; - void *data; -} IMAGE_INFO; - -typedef struct -{ - int width, height; - int red, green, blue; -} VIDEO_MODE; - -typedef struct -{ - int sorted_index; - int server_index; - - int progression; - int game_type; - int max_players; - int num_players; - int flags; - int latency; /* in ms */ - char name[64]; - char map[32]; - char version[32]; - char address[24]; - char player_names[16][48]; - int player_scores[16]; -} SERVER_INFO; - -struct rect -{ - float x, y, w, h; -}; - -struct rect *ui_screen(); -typedef void (*rect_fun)(const struct rect *r); -void ui_foreach_rect(rect_fun fun); -void ui_scale(float scale); -void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); -void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom); -void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right); -void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right); -void ui_margin(const struct rect *original, int pixels, struct rect *new_rect); - -/* image loaders */ -int gfx_load_png(IMAGE_INFO *img, const char *filename); - -/* - Group: Graphics -*/ - -int gfx_init(); -void gfx_shutdown(); -void gfx_swap(); - -float gfx_screenaspect(); - -int gfx_get_video_modes(VIDEO_MODE *list, int maxcount); -void gfx_set_vsync(int val); - -int gfx_window_active(); -int gfx_window_open(); - -/* textures */ -/* - Function: gfx_load_texture - Loads a texture from a file. TGA and PNG supported. - - Arguments: - filename - Null terminated string to the file to load. - - Returns: - An ID to the texture. -1 on failure. - - See Also: - -*/ -int gfx_load_texture(const char *filename); - -/* - Function: gfx_load_texture_raw - Loads a texture from memory. - - Arguments: - w - Width of the texture. - h - Height of the texture. - data - Pointer to the pixel data. - - Returns: - An ID to the texture. -1 on failure. - - Remarks: - The pixel data should be in RGBA format with 8 bit per component. - So the total size of the data should be w*h*4. - - See Also: - -*/ -int gfx_load_texture_raw(int w, int h, int format, const void *data); -/*int gfx_load_mip_texture_raw(int w, int h, int format, const void *data);*/ - -/* - Function: gfx_texture_set - Sets the active texture. - - Arguments: - id - ID to the texture to set. -*/ -void gfx_texture_set(int id); - -/* - Function: gfx_unload_texture - Unloads a texture. - - Arguments: - id - ID to the texture to unload. - - See Also: - , - - Remarks: - NOT IMPLEMENTED -*/ -int gfx_unload_texture(int id); - -void gfx_clear(float r, float g, float b); - -/* - Function: gfx_screenwidth - Returns the screen width. - - See Also: - -*/ -int gfx_screenwidth(); - -/* - Function: gfx_screenheight - Returns the screen height. - - See Also: - -*/ -int gfx_screenheight(); - -/* - Function: gfx_mapscreen - Specifies the coordinate system for the screen. - - Arguments: - tl_x - Top-left X - tl_y - Top-left Y - br_x - Bottom-right X - br_y - Bottom-right y -*/ -void gfx_mapscreen(float tl_x, float tl_y, float br_x, float br_y); - -/* - Function: gfx_blend_normal - Set the active blending mode to normal (src, 1-src). - - Remarks: - This must be used before calling . - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA). - - See Also: - -*/ -void gfx_blend_normal(); - -/* - Function: gfx_blend_additive - Set the active blending mode to additive (src, one). - - Remarks: - This must be used before calling . - This is equal to glBlendFunc(GL_SRC_ALPHA, GL_ONE). - - See Also: - -*/ -void gfx_blend_additive(); - -/* - Function: gfx_quads_begin - Begins a quad drawing session. - - Remarks: - This functions resets the rotation, color and subset. - End the session by using . - You can't change texture or blending mode during a session. - - See Also: - -*/ -void gfx_quads_begin(); - -/* - Function: gfx_quads_end - Ends a quad session. - - See Also: - -*/ -void gfx_quads_end(); - -/* - Function: gfx_quads_setrotation - Sets the rotation to use when drawing a quad. - - Arguments: - angle - Angle in radians. - - Remarks: - The angle is reset when is called. -*/ -void gfx_quads_setrotation(float angle); - -/* - Function: gfx_quads_setcolorvertex - Sets the color of a vertex. - - Arguments: - i - Index to the vertex. - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when is called. -*/ -void gfx_setcolorvertex(int i, float r, float g, float b, float a); - -/* - Function: gfx_quads_setcolor - Sets the color of all the vertices. - - Arguments: - r - Red value. - g - Green value. - b - Blue value. - a - Alpha value. - - Remarks: - The color values are from 0.0 to 1.0. - The color is reset when is called. -*/ -void gfx_setcolor(float r, float g, float b, float a); - -/* - Function: gfx_quads_setsubset - Sets the uv coordinates to use. - - Arguments: - tl_u - Top-left U value. - tl_v - Top-left V value. - br_u - Bottom-right U value. - br_v - Bottom-right V value. - - Remarks: - O,0 is top-left of the texture and 1,1 is bottom-right. - The color is reset when is called. -*/ -void gfx_quads_setsubset(float tl_u, float tl_v, float br_u, float br_v); - -/* - Function: gfx_quads_drawTL - Draws a quad by specifying the top-left point. - - Arguments: - x - X coordinate of the top-left corner. - y - Y coordinate of the top-left corner. - width - Width of the quad. - height - Height of the quad. - - Remarks: - Rotation still occurs from the center of the quad. - You must call before calling this function. - - See Also: - -*/ -void gfx_quads_drawTL(float x, float y, float width, float height); - -/* - Function: gfx_quads_draw - Draws a quad by specifying the center point. - - Arguments: - x - X coordinate of the center. - y - Y coordinate of the center. - width - Width of the quad. - height - Height of the quad. - - Remarks: - You must call before calling this function. - - See Also: - -*/ -void gfx_quads_draw(float x, float y, float w, float h); - -void gfx_quads_draw_freeform( - float x0, float y0, - float x1, float y1, - float x2, float y2, - float x3, float y3); - -void gfx_quads_text(float x, float y, float size, const char *text); - -/* sound (client) */ -int snd_init(); - -void snd_set_channel(int cid, float vol, float pan); - -int snd_load_wv(const char *filename); - -int snd_play_at(int cid, int sid, int flags, float x, float y); -int snd_play(int cid, int sid, int flags); - -void snd_stop(int id); -void snd_set_listener_pos(float x, float y); - -int snd_shutdown(); - -/* - Group: Input -*/ - -/* - Function: inp_mouse_relative - Fetches the mouse movements. - - Arguments: - x - Pointer to the variable that should get the X movement. - y - Pointer to the variable that should get the Y movement. -*/ -void inp_mouse_relative(int *x, int *y); - -int inp_mouse_scroll(); - -/* - Function: inp_key_pressed - Checks if a key is pressed. - - Arguments: - key - Index to the key to check - - Returns: - Returns 1 if the button is pressed, otherwise 0. - - Remarks: - Check keys.h for the keys. -*/ -int inp_key_pressed(int key); - -/* - Group: Map -*/ - -int map_load(const char *mapname); -void map_unload(); - -/* - Function: map_is_loaded - Checks if a map is loaded. - - Returns: - Returns 1 if the button is pressed, otherwise 0. -*/ -int map_is_loaded(); - -/* - Function: map_num_items - Checks the number of items in the loaded map. - - Returns: - Returns the number of items. 0 if no map is loaded. -*/ -int map_num_items(); - -/* - Function: map_find_item - Searches the map for an item. - - Arguments: - type - Item type. - id - Item ID. - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_find_item(int type, int id); - -/* - Function: map_get_item - Gets an item from the loaded map from index. - - Arguments: - index - Item index. - type - Pointer that recives the item type (can be NULL). - id - Pointer that recives the item id (can be NULL). - - Returns: - Returns a pointer to the item if it exists, otherwise it returns NULL. -*/ -void *map_get_item(int index, int *type, int *id); - -/* - Function: map_get_type - Gets the index range of an item type. - - Arguments: - type - Item type to search for. - start - Pointer that recives the starting index. - num - Pointer that recives the number of items. - - Returns: - If the item type is not in the map, start and num will be set to 0. -*/ -void map_get_type(int type, int *start, int *num); - -/* - Function: map_get_data - Fetches a pointer to a raw data chunk in the map. - - Arguments: - index - Index to the data to fetch. - - Returns: - A pointer to the raw data, otherwise 0. -*/ -void *map_get_data(int index); - -/* - Group: Network (Server) -*/ -/* - Function: snap_new_item - Creates a new item that should be sent. - - Arguments: - type - Type of the item. - id - ID of the item. - size - Size of the item. - - Returns: - A pointer to the item data, otherwise 0. - - Remarks: - The item data should only consist pf 4 byte integers as - they are subject to byte swapping. This means that the size - argument should be dividable by 4. -*/ -void *snap_new_item(int type, int id, int size); - -/* - Group: Network (Client) -*/ -/* - Function: snap_num_items - Check the number of items in a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - - Returns: - The number of items in the snapshot. -*/ -int snap_num_items(int snapid); - -/* - Function: snap_get_item - Gets an item from a snapshot. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - index - Index of the item. - item - Pointer that recives the item info. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -const void *snap_get_item(int snapid, int index, SNAP_ITEM *item); - -/* - Function: snap_find_item - Searches a snapshot for an item. - - Arguments: - snapid - Snapshot ID to the data to fetch. - * SNAP_PREV for previous snapshot. - * SNAP_CUR for current snapshot. - type - Type of the item. - id - ID of the item. - - Returns: - Returns a pointer to the item if it exists, otherwise NULL. -*/ -const void *snap_find_item(int snapid, int type, int id); - -/* - Function: snap_input - Sets the input data to send to the server. - - Arguments: - data - Pointer to the data. - size - Size of the data. - - Remarks: - The data should only consist of 4 bytes integer as they are - subject to byte swapping. -*/ -void snap_input(void *data, int size); - -/* - Group: Server Callbacks -*/ -/* - Function: mods_init - Called when the server is started. - - Remarks: - It's called after the map is loaded so all map items are available. -*/ -void mods_init(); - -/* - Function: mods_shutdown - Called when the server quits. - - Remarks: - Should be used to clean up all resources used. -*/ -void mods_shutdown(); - -/* - Function: mods_client_enter - Called when a client has joined the game. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - It's called when the client is finished loading and should enter gameplay. -*/ -void mods_client_enter(int cid); - -/* - Function: mods_client_drop - Called when a client drops from the server. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS -*/ -void mods_client_drop(int cid); - -/* - Function: mods_client_input - Called when the server recives new input from a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - input - Pointer to the input data. - size - Size of the data. (NOT IMPLEMENTED YET) -*/ -void mods_client_input(int cid, void *input); - -/* - Function: mods_tick - Called with a regular interval to progress the gameplay. - - Remarks: - The SERVER_TICK_SPEED tells the number of ticks per second. -*/ -void mods_tick(); - -/* - Function: mods_presnap - Called before the server starts to construct snapshots for the clients. -*/ -void mods_presnap(); - -/* - Function: mods_snap - Called to create the snapshot for a client. - - Arguments: - cid - Client ID. Is 0 - MAX_CLIENTS. - - Remarks: - The game should make a series of calls to to construct - the snapshot for the client. -*/ -void mods_snap(int cid); - -/* - Function: mods_postsnap - Called after the server is done sending the snapshots. -*/ -void mods_postsnap(); - -/* - Group: Client Callbacks -*/ -/* - Function: modc_init - Called when the client starts. - - Remarks: - The game should load resources that are used during the entire - time of the game. No map is loaded. -*/ -void modc_init(); - -/* - Function: modc_newsnapshot - Called when the client progressed to a new snapshot. - - Remarks: - The client can check for items in the snapshot and perform one time - events like playing sounds, spawning client side effects etc. -*/ -void modc_newsnapshot(); - -/* - Function: modc_entergame - Called when the client has successfully connect to a server and - loaded a map. - - Remarks: - The client can check for items in the map and load them. -*/ -void modc_entergame(); - -/* - Function: modc_shutdown - Called when the client closes down. -*/ -void modc_shutdown(); - -/* - Function: modc_render - Called every frame to let the game render it self. -*/ -void modc_render(); - -/* - Function: modc_statechange - Called every time client changes state. -*/ -void modc_statechange(int new_state, int old_state); - -/* undocumented callbacks */ -void modc_connected(); -void modc_message(int msg); -void modc_predict(); - -void mods_message(int msg, int client_id); -void mods_connected(int client_id); - - -const char *modc_net_version(); -const char *mods_net_version(); - -/* server */ -int server_getclientinfo(int client_id, CLIENT_INFO *info); -const char *server_clientname(int client_id); -void server_setclientname(int client_id, const char *name); - -void server_setbrowseinfo(int game_type, int progression); - -int server_tick(); -int server_tickspeed(); - -/* input */ -int inp_key_was_pressed(int key); -int inp_key_down(int key); -char inp_last_char(); -int inp_last_key(); -void inp_clear(); -void inp_update(); -void inp_init(); -void inp_mouse_mode_absolute(); -void inp_mouse_mode_relative(); -int inp_mouse_doubleclick(); - -int inp_key_presses(int key); -int inp_key_releases(int key); -int inp_key_state(int key); - -const char *inp_key_name(int k); -int inp_key_code(const char *key_name); - -/* message packing */ -enum -{ - MSGFLAG_VITAL=1 -}; - -void msg_pack_start_system(int msg, int flags); -void msg_pack_start(int msg, int flags); -void msg_pack_int(int i); -void msg_pack_string(const char *p, int limit); -void msg_pack_raw(const void *data, int size); -void msg_pack_end(); - -typedef struct -{ - int msg; - int flags; - const unsigned char *data; - int size; -} MSG_INFO; - -const MSG_INFO *msg_get_info(); - -/* message unpacking */ -int msg_unpack_start(const void *data, int data_size, int *system); -int msg_unpack_int(); -const char *msg_unpack_string(); -const unsigned char *msg_unpack_raw(int size); - -/* message sending */ -int server_send_msg(int client_id); /* client_id == -1 == broadcast */ -int client_send_msg(); - -/* client */ -int client_tick(); -int client_predtick(); -float client_intratick(); -float client_intrapredtick(); -int client_tickspeed(); -float client_frametime(); -float client_localtime(); - -int client_state(); -const char *client_error_string(); -int *client_get_input(int tick); -int client_connection_problems(); - -void client_connect(const char *address); -void client_disconnect(); -void client_quit(); -void client_entergame(); - -void client_rcon(const char *cmd); - - -void client_serverbrowse_refresh(int lan); - -SERVER_INFO *client_serverbrowse_sorted_get(int index); -int client_serverbrowse_sorted_num(); - -SERVER_INFO *client_serverbrowse_get(int index); -int client_serverbrowse_num(); - -int client_serverbrowse_num_requests(); - -void client_serverbrowse_update(); - -/* undocumented graphics stuff */ - -void gfx_pretty_text_color(float r, float g, float b, float a); -void gfx_pretty_text(float x, float y, float size, const char *text, int max_width); -float gfx_pretty_text_width(float size, const char *text, int length); - -void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y); -int gfx_memory_usage(); -void gfx_screenshot(); - -void gfx_lines_begin(); -void gfx_lines_draw(float x0, float y0, float x1, float y1); -void gfx_lines_end(); - -void gfx_mask_op(int mask, int write); -void gfx_clear_mask(int fill); - -/* server snap id */ -int snap_new_id(); -void snap_free_id(int id); - -/* other */ -void map_unload_data(int index); -void map_set(void *m); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/src/engine/keynames.c b/src/engine/keynames.c deleted file mode 100644 index fa87467d..00000000 --- a/src/engine/keynames.c +++ /dev/null @@ -1,524 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ - -#include - -static const char key_strings[512][16] = -{ - "#0", - "#1", - "#2", - "#3", - "#4", - "#5", - "#6", - "#7", - "#8", - "#9", - "#10", - "#11", - "#12", - "#13", - "#14", - "#15", - "#16", - "#17", - "#18", - "#19", - "#20", - "#21", - "#22", - "#23", - "#24", - "#25", - "#26", - "#27", - "#28", - "#29", - "#30", - "#31", - "space", - "#33", - "#34", - "#35", - "#36", - "#37", - "#38", - "#39", - "#40", - "#41", - "#42", - "#43", - "#44", - "#45", - "#46", - "#47", - "0", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "#58", - "#59", - "#60", - "#61", - "#62", - "#63", - "#64", - "a", - "b", - "c", - "d", - "e", - "f", - "g", - "h", - "i", - "j", - "k", - "l", - "m", - "n", - "o", - "p", - "q", - "r", - "s", - "t", - "u", - "v", - "w", - "x", - "y", - "z", - "#91", - "#92", - "#93", - "#94", - "#95", - "#96", - "#97", - "#98", - "#99", - "#100", - "#101", - "#102", - "#103", - "#104", - "#105", - "#106", - "#107", - "#108", - "#109", - "#110", - "#111", - "#112", - "#113", - "#114", - "#115", - "#116", - "#117", - "#118", - "#119", - "#120", - "#121", - "#122", - "#123", - "#124", - "#125", - "#126", - "#127", - "#128", - "#129", - "#130", - "#131", - "#132", - "#133", - "#134", - "#135", - "#136", - "#137", - "#138", - "#139", - "#140", - "#141", - "#142", - "#143", - "#144", - "#145", - "#146", - "#147", - "#148", - "#149", - "#150", - "#151", - "#152", - "#153", - "#154", - "#155", - "#156", - "#157", - "#158", - "#159", - "#160", - "#161", - "#162", - "#163", - "#164", - "#165", - "#166", - "#167", - "#168", - "#169", - "#170", - "#171", - "#172", - "#173", - "#174", - "#175", - "#176", - "#177", - "#178", - "#179", - "#180", - "#181", - "#182", - "#183", - "#184", - "#185", - "#186", - "#187", - "#188", - "#189", - "#190", - "#191", - "#192", - "#193", - "#194", - "#195", - "#196", - "#197", - "#198", - "#199", - "#200", - "#201", - "#202", - "#203", - "#204", - "#205", - "#206", - "#207", - "#208", - "#209", - "#210", - "#211", - "#212", - "#213", - "#214", - "#215", - "#216", - "#217", - "#218", - "#219", - "#220", - "#221", - "#222", - "#223", - "#224", - "#225", - "#226", - "#227", - "#228", - "#229", - "#230", - "#231", - "#232", - "#233", - "#234", - "#235", - "#236", - "#237", - "#238", - "#239", - "#240", - "#241", - "#242", - "#243", - "#244", - "#245", - "#246", - "#247", - "#248", - "#249", - "#250", - "#251", - "#252", - "#253", - "#254", - "#255", - "special", - "esc", - "f1", - "f2", - "f3", - "f4", - "f5", - "f6", - "f7", - "f8", - "f9", - "f10", - "f11", - "f12", - "f13", - "f14", - "f15", - "f16", - "f17", - "f18", - "f19", - "f20", - "f21", - "f22", - "f23", - "f24", - "f25", - "up", - "down", - "left", - "right", - "lshift", - "rshift", - "lctrl", - "rctrl", - "lalt", - "ralt", - "tab", - "enter", - "backspace", - "insert", - "del", - "pageup", - "pagedown", - "home", - "end", - "kp0", - "kp1", - "kp2", - "kp3", - "kp4", - "kp5", - "kp6", - "kp7", - "kp8", - "kp9", - "kpdivide", - "kpmultiply", - "kpsubtract", - "kpadd", - "kpdecimal", - "kpequal", - "kpenter", - "#319", - "#320", - "#321", - "#322", - "#323", - "#324", - "#325", - "#326", - "#327", - "#328", - "#329", - "#330", - "#331", - "#332", - "#333", - "#334", - "#335", - "#336", - "#337", - "#338", - "#339", - "#340", - "#341", - "#342", - "#343", - "#344", - "#345", - "#346", - "#347", - "#348", - "#349", - "#350", - "#351", - "#352", - "#353", - "#354", - "#355", - "#356", - "#357", - "#358", - "#359", - "#360", - "#361", - "#362", - "#363", - "#364", - "#365", - "#366", - "#367", - "#368", - "#369", - "#370", - "#371", - "#372", - "#373", - "#374", - "#375", - "#376", - "#377", - "#378", - "#379", - "#380", - "#381", - "mousewheeldown", - "mousewheelup", - "mouse1", - "mouse2", - "mouse3", - "mouse4", - "mouse5", - "mouse6", - "mouse7", - "mouse8", - "#392", - "#393", - "#394", - "#395", - "#396", - "#397", - "#398", - "#399", - "#400", - "#401", - "#402", - "#403", - "#404", - "#405", - "#406", - "#407", - "#408", - "#409", - "#410", - "#411", - "#412", - "#413", - "#414", - "#415", - "#416", - "#417", - "#418", - "#419", - "#420", - "#421", - "#422", - "#423", - "#424", - "#425", - "#426", - "#427", - "#428", - "#429", - "#430", - "#431", - "#432", - "#433", - "#434", - "#435", - "#436", - "#437", - "#438", - "#439", - "#440", - "#441", - "#442", - "#443", - "#444", - "#445", - "#446", - "#447", - "#448", - "#449", - "#450", - "#451", - "#452", - "#453", - "#454", - "#455", - "#456", - "#457", - "#458", - "#459", - "#460", - "#461", - "#462", - "#463", - "#464", - "#465", - "#466", - "#467", - "#468", - "#469", - "#470", - "#471", - "#472", - "#473", - "#474", - "#475", - "#476", - "#477", - "#478", - "#479", - "#480", - "#481", - "#482", - "#483", - "#484", - "#485", - "#486", - "#487", - "#488", - "#489", - "#490", - "#491", - "#492", - "#493", - "#494", - "#495", - "#496", - "#497", - "#498", - "#499", - "#500", - "#501", - "#502", - "#503", - "#504", - "#505", - "#506", - "#507", - "#508", - "#509", - "#510", - "#511", -}; - -const char *inp_key_name(int k) { if (k >= 0 && k < 512) return key_strings[k]; else return key_strings[0]; } -int inp_key_code(const char *key_name) { int i; if (!strcmp(key_name, "-?-")) return -1; else for (i = 0; i < 512; i++) if (!strcmp(key_strings[i], key_name)) return i; return -1; } - diff --git a/src/engine/keys.h b/src/engine/keys.h deleted file mode 100644 index 77a52f58..00000000 --- a/src/engine/keys.h +++ /dev/null @@ -1,88 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_KEYS_H -#define ENGINE_KEYS_H -/* AUTO GENERATED! DO NOT EDIT MANUALLY! */ - -enum -{ - KEY_UNKNOWN = -1, - KEY_SPACE = 32, - KEY_SPECIAL = 256, - KEY_ESC = (KEY_SPECIAL+1), - KEY_F1 = (KEY_SPECIAL+2), - KEY_F2 = (KEY_SPECIAL+3), - KEY_F3 = (KEY_SPECIAL+4), - KEY_F4 = (KEY_SPECIAL+5), - KEY_F5 = (KEY_SPECIAL+6), - KEY_F6 = (KEY_SPECIAL+7), - KEY_F7 = (KEY_SPECIAL+8), - KEY_F8 = (KEY_SPECIAL+9), - KEY_F9 = (KEY_SPECIAL+10), - KEY_F10 = (KEY_SPECIAL+11), - KEY_F11 = (KEY_SPECIAL+12), - KEY_F12 = (KEY_SPECIAL+13), - KEY_F13 = (KEY_SPECIAL+14), - KEY_F14 = (KEY_SPECIAL+15), - KEY_F15 = (KEY_SPECIAL+16), - KEY_F16 = (KEY_SPECIAL+17), - KEY_F17 = (KEY_SPECIAL+18), - KEY_F18 = (KEY_SPECIAL+19), - KEY_F19 = (KEY_SPECIAL+20), - KEY_F20 = (KEY_SPECIAL+21), - KEY_F21 = (KEY_SPECIAL+22), - KEY_F22 = (KEY_SPECIAL+23), - KEY_F23 = (KEY_SPECIAL+24), - KEY_F24 = (KEY_SPECIAL+25), - KEY_F25 = (KEY_SPECIAL+26), - KEY_UP = (KEY_SPECIAL+27), - KEY_DOWN = (KEY_SPECIAL+28), - KEY_LEFT = (KEY_SPECIAL+29), - KEY_RIGHT = (KEY_SPECIAL+30), - KEY_LSHIFT = (KEY_SPECIAL+31), - KEY_RSHIFT = (KEY_SPECIAL+32), - KEY_LCTRL = (KEY_SPECIAL+33), - KEY_RCTRL = (KEY_SPECIAL+34), - KEY_LALT = (KEY_SPECIAL+35), - KEY_RALT = (KEY_SPECIAL+36), - KEY_TAB = (KEY_SPECIAL+37), - KEY_ENTER = (KEY_SPECIAL+38), - KEY_BACKSPACE = (KEY_SPECIAL+39), - KEY_INSERT = (KEY_SPECIAL+40), - KEY_DEL = (KEY_SPECIAL+41), - KEY_PAGEUP = (KEY_SPECIAL+42), - KEY_PAGEDOWN = (KEY_SPECIAL+43), - KEY_HOME = (KEY_SPECIAL+44), - KEY_END = (KEY_SPECIAL+45), - KEY_KP_0 = (KEY_SPECIAL+46), - KEY_KP_1 = (KEY_SPECIAL+47), - KEY_KP_2 = (KEY_SPECIAL+48), - KEY_KP_3 = (KEY_SPECIAL+49), - KEY_KP_4 = (KEY_SPECIAL+50), - KEY_KP_5 = (KEY_SPECIAL+51), - KEY_KP_6 = (KEY_SPECIAL+52), - KEY_KP_7 = (KEY_SPECIAL+53), - KEY_KP_8 = (KEY_SPECIAL+54), - KEY_KP_9 = (KEY_SPECIAL+55), - KEY_KP_DIVIDE = (KEY_SPECIAL+56), - KEY_KP_MULTIPLY = (KEY_SPECIAL+57), - KEY_KP_SUBTRACT = (KEY_SPECIAL+58), - KEY_KP_ADD = (KEY_SPECIAL+59), - KEY_KP_DECIMAL = (KEY_SPECIAL+60), - KEY_KP_EQUAL = (KEY_SPECIAL+61), - KEY_KP_ENTER = (KEY_SPECIAL+62), - KEY_REPEAT = 0x00030005, - KEY_MOUSE_WHEEL_DOWN = 382, - KEY_MOUSE_WHEEL_UP = 383, - KEY_MOUSE_FIRST = 384, - KEY_MOUSE_1 = KEY_MOUSE_FIRST+0, - KEY_MOUSE_2 = KEY_MOUSE_FIRST+1, - KEY_MOUSE_3 = KEY_MOUSE_FIRST+2, - KEY_MOUSE_4 = KEY_MOUSE_FIRST+3, - KEY_MOUSE_5 = KEY_MOUSE_FIRST+4, - KEY_MOUSE_6 = KEY_MOUSE_FIRST+5, - KEY_MOUSE_7 = KEY_MOUSE_FIRST+6, - KEY_MOUSE_8 = KEY_MOUSE_FIRST+7, - KEY_LAST -}; - -#endif diff --git a/src/engine/map.c b/src/engine/map.c deleted file mode 100644 index 921c05c7..00000000 --- a/src/engine/map.c +++ /dev/null @@ -1,61 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include "datafile.h" - -static DATAFILE *map = 0; - -void *map_get_data(int index) -{ - return datafile_get_data(map, index); -} - -void map_unload_data(int index) -{ - datafile_unload_data(map, index); -} - -void *map_get_item(int index, int *type, int *id) -{ - return datafile_get_item(map, index, type, id); -} - -void map_get_type(int type, int *start, int *num) -{ - datafile_get_type(map, type, start, num); -} - -void *map_find_item(int type, int id) -{ - return datafile_find_item(map, type, id); -} - -int map_num_items() -{ - return datafile_num_items(map); -} - -void map_unload() -{ - datafile_unload(map); - map = 0x0; -} - -int map_is_loaded() -{ - return map != 0; -} - -int map_load(const char *mapname) -{ - char buf[512]; - sprintf(buf, "data/maps/%s.map", mapname); - map = datafile_load(buf); - return map != 0; -} - -void map_set(void *m) -{ - if(map) - map_unload(); - map = (DATAFILE*)m; -} diff --git a/src/engine/memheap.c b/src/engine/memheap.c deleted file mode 100644 index cb8ca664..00000000 --- a/src/engine/memheap.c +++ /dev/null @@ -1,102 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" - -typedef struct CHUNK_t -{ - char *memory; - char *current; - char *end; - struct CHUNK_t *next; -} CHUNK; - -typedef struct -{ - CHUNK *current; -} HEAP; - -/* how large each chunk should be */ -static const int chunksize = 1024*64; - -/* allocates a new chunk to be used */ -static CHUNK *memheap_newchunk() -{ - CHUNK *chunk; - char *mem; - - /* allocate memory */ - mem = (char*)mem_alloc(sizeof(CHUNK)+chunksize, 1); - if(!mem) - return 0x0; - - /* the chunk structure is located in the begining of the chunk */ - /* init it and return the chunk */ - chunk = (CHUNK*)mem; - chunk->memory = (char*)(chunk+1); - chunk->current = chunk->memory; - chunk->end = chunk->memory + chunksize; - chunk->next = (CHUNK *)0x0; - return chunk; -} - -/******************/ -static void *memheap_allocate_from_chunk(CHUNK *chunk, int size) -{ - char *mem; - - /* check if we need can fit the allocation */ - if(chunk->current + size >= chunk->end) - return (void*)0x0; - - /* get memory and move the pointer forward */ - mem = chunk->current; - chunk->current += size; - return mem; -} - -/* creates a heap */ -HEAP *memheap_create() -{ - CHUNK *chunk; - HEAP *heap; - - /* allocate a chunk and allocate the heap structure on that chunk */ - chunk = memheap_newchunk(); - heap = (HEAP *)memheap_allocate_from_chunk(chunk, sizeof(HEAP)); - heap->current = chunk; - return heap; -} - -/* destroys the heap */ -void memheap_destroy(HEAP *heap) -{ - CHUNK *chunk = heap->current; - CHUNK *next; - - while(chunk) - { - next = chunk->next; - mem_free(chunk); - chunk = next; - } -} - -/* */ -void *memheap_allocate(HEAP *heap, int size) -{ - char *mem; - - /* try to allocate from current chunk */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - if(!mem) - { - /* allocate new chunk and add it to the heap */ - CHUNK *chunk = memheap_newchunk(); - chunk->next = heap->current; - heap->current = chunk; - - /* try to allocate again */ - mem = (char *)memheap_allocate_from_chunk(heap->current, size); - } - - return mem; -} diff --git a/src/engine/memheap.h b/src/engine/memheap.h deleted file mode 100644 index 83082046..00000000 --- a/src/engine/memheap.h +++ /dev/null @@ -1,6 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -typedef struct HEAP_t HEAP; -HEAP *memheap_create(); -void memheap_destroy(HEAP *heap); -void *memheap_allocate(HEAP *heap, int size); diff --git a/src/engine/msg.c b/src/engine/msg.c deleted file mode 100644 index d3f916fa..00000000 --- a/src/engine/msg.c +++ /dev/null @@ -1,55 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "interface.h" -#include "packer.h" - -/* message packing */ -static PACKER msg_packer; -static MSG_INFO pack_info; - -void msg_pack_int(int i) { packer_add_int(&msg_packer, i); } -void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); } -void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); } - -void msg_pack_start_system(int msg, int flags) -{ - packer_reset(&msg_packer); - pack_info.msg = (msg<<1)|1; - pack_info.flags = flags; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_start(int msg, int flags) -{ - packer_reset(&msg_packer); - pack_info.msg = msg<<1; - pack_info.flags = flags; - - msg_pack_int(pack_info.msg); -} - -void msg_pack_end() -{ - pack_info.size = packer_size(&msg_packer); - pack_info.data = packer_data(&msg_packer); -} - -const MSG_INFO *msg_get_info() -{ - return &pack_info; -} - -/* message unpacking */ -static UNPACKER msg_unpacker; -int msg_unpack_start(const void *data, int data_size, int *system) -{ - int msg; - unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size); - msg = msg_unpack_int(); - *system = msg&1; - return msg>>1; -} - -int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); } -const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); } -const unsigned char *msg_unpack_raw(int size) { return unpacker_get_raw(&msg_unpacker, size); } diff --git a/src/engine/network.c b/src/engine/network.c deleted file mode 100644 index 85b4604f..00000000 --- a/src/engine/network.c +++ /dev/null @@ -1,937 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include - -#include "system.h" -#include "config.h" -#include "network.h" - -/* - header (6 bytes) - unsigned char flags; 1 - unsigned char seq_ack[3]; 4 - unsigned char token[2]; 6 -*/ - -enum -{ - NETWORK_VERSION = 1, - - NETWORK_HEADER_SIZE = 6, - NETWORK_MAX_PAYLOAD = 1024, - NETWORK_MAX_PACKET_SIZE = NETWORK_HEADER_SIZE+NETWORK_MAX_PAYLOAD, - NETWORK_MAX_CLIENTS = 16, - - NETWORK_CONNSTATE_OFFLINE=0, - NETWORK_CONNSTATE_CONNECT=1, - NETWORK_CONNSTATE_CONNECTACCEPTED=2, - NETWORK_CONNSTATE_ONLINE=3, - NETWORK_CONNSTATE_ERROR=4, - - NETWORK_PACKETFLAG_CONNECT=0x01, - NETWORK_PACKETFLAG_ACCEPT=0x02, - NETWORK_PACKETFLAG_CLOSE=0x04, - NETWORK_PACKETFLAG_VITAL=0x08, - NETWORK_PACKETFLAG_RESEND=0x10, - NETWORK_PACKETFLAG_CONNLESS=0x20, - - NETWORK_MAX_SEQACK=0x1000 -}; - -static int current_token = 1; - -typedef struct -{ - unsigned char ID[2]; - unsigned char version; - unsigned char flags; - unsigned short seq; - unsigned short ack; - unsigned crc; - int token; - unsigned data_size; - int64 first_send_time; - unsigned char *data; -} NETPACKETDATA; - - -static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet) -{ - unsigned char buffer[NETWORK_MAX_PACKET_SIZE]; - int send_size = NETWORK_HEADER_SIZE+packet->data_size; - - buffer[0] = packet->flags; - buffer[1] = ((packet->seq>>4)&0xf0) | ((packet->ack>>8)&0x0f); - buffer[2] = packet->seq; - buffer[3] = packet->ack; - buffer[4] = packet->token>>8; - buffer[5] = packet->token&0xff; - mem_copy(buffer+NETWORK_HEADER_SIZE, packet->data, packet->data_size); - net_udp4_send(socket, addr, buffer, send_size); -} - -typedef struct RINGBUFFER_ITEM_t -{ - struct RINGBUFFER_ITEM_t *next; - struct RINGBUFFER_ITEM_t *prev; - int size; -} RINGBUFFER_ITEM; - -typedef struct -{ - RINGBUFFER_ITEM *first; - RINGBUFFER_ITEM *last; - unsigned buffer_size; -} RINGBUFFER; - -static void rb_init(RINGBUFFER *rb) -{ - rb->first = 0; - rb->last = 0; - rb->buffer_size = 0; -} - -static void *rb_item_data(RINGBUFFER_ITEM *item) -{ - return (void*)(item+1); -} - -static void *rb_alloc(RINGBUFFER *rb, int size) -{ - RINGBUFFER_ITEM *item = (RINGBUFFER_ITEM*)mem_alloc(sizeof(RINGBUFFER_ITEM)+size, 1); - item->size = size; - - item->prev = rb->last; - item->next = 0; - if(rb->last) - rb->last->next = item; - else - rb->first = item; - rb->last = item; - - rb->buffer_size += size; - return rb_item_data(item); -} - -static void rb_pop_first(RINGBUFFER *rb) -{ - if(rb->first) - { - RINGBUFFER_ITEM *next = rb->first->next; - rb->buffer_size -= rb->first->size; - mem_free(rb->first); - rb->first = next; - if(rb->first) - rb->first->prev = NULL; - else - rb->last = NULL; - } -} - -static void rb_clear(RINGBUFFER *rb) -{ - while(rb->first) - rb_pop_first(rb); -} - - -typedef struct -{ - unsigned short seq; - unsigned short ack; - unsigned state; - - int token; - - int remote_closed; - - int connected; - int disconnected; - - RINGBUFFER buffer; - - int64 last_update_time; - int64 last_recv_time; - int64 last_send_time; - - char error_string[256]; - - NETADDR4 peeraddr; - NETSOCKET socket; - NETSTATS stats; -} NETCONNECTION; - -typedef struct -{ - NETCONNECTION conn; -} NETSLOT; - -struct NETSERVER_t -{ - 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]; -} ; - -struct NETCLIENT_t -{ - NETADDR4 server_addr; - NETSOCKET socket; - unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE]; - - NETCONNECTION conn; -}; - -static void conn_reset_stats(NETCONNECTION *conn) -{ - mem_zero(&conn->stats, sizeof(conn->stats)); -} - -static void conn_reset(NETCONNECTION *conn) -{ - conn->seq = 0; - conn->ack = 0; - conn->remote_closed = 0; - - if(conn->state == NETWORK_CONNSTATE_ONLINE || - conn->state == NETWORK_CONNSTATE_ERROR) - { - conn->disconnected++; - } - - conn->state = NETWORK_CONNSTATE_OFFLINE; - conn->last_send_time = 0; - conn->last_recv_time = 0; - conn->last_update_time = 0; - conn->token = -1; - - rb_clear(&conn->buffer); -} - - -static const char *conn_error(NETCONNECTION *conn) -{ - return conn->error_string; -} - -static void conn_set_error(NETCONNECTION *conn, const char *str) -{ - strcpy(conn->error_string, str); -} - -/* -static int conn_state(NETCONNECTION *conn) -{ - return conn->state; -}*/ - -static void conn_init(NETCONNECTION *conn, NETSOCKET socket) -{ - conn_reset(conn); - conn_reset_stats(conn); - conn->socket = socket; - conn->connected = 0; - conn->disconnected = 0; - rb_init(&conn->buffer); - mem_zero(conn->error_string, sizeof(conn->error_string)); -} - -static void conn_ack(NETCONNECTION *conn, int ack) -{ - while(1) - { - RINGBUFFER_ITEM *item = conn->buffer.first; - NETPACKETDATA *resend; - if(!item) - break; - - resend = (NETPACKETDATA *)rb_item_data(item); - if(resend->seq <= ack || (ack < NETWORK_MAX_SEQACK/3 && resend->seq > NETWORK_MAX_SEQACK/2)) - rb_pop_first(&conn->buffer); - else - break; - } -} - -static void conn_send_raw(NETCONNECTION *conn, NETPACKETDATA *data) -{ - conn->last_send_time = time_get(); - conn->stats.send_packets++; - conn->stats.send_bytes += data->data_size + NETWORK_HEADER_SIZE; - send_packet(conn->socket, &conn->peeraddr, data); -} - -static void conn_resend(NETCONNECTION *conn) -{ - RINGBUFFER_ITEM *item = conn->buffer.first; - while(item) - { - NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); - conn->stats.resend_packets++; - conn->stats.resend_bytes += resend->data_size + NETWORK_HEADER_SIZE; - conn_send_raw(conn, resend); - item = item->next; - } -} - -static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data) -{ - NETPACKETDATA p; - - if(flags&NETWORK_PACKETFLAG_VITAL) - conn->seq = (conn->seq+1)%NETWORK_MAX_SEQACK; - - p.ID[0] = 'T'; - p.ID[1] = 'W'; - p.version = NETWORK_VERSION; - p.flags = flags; - p.seq = conn->seq; - p.ack = conn->ack; - p.crc = 0; - p.token = conn->token; - p.data_size = data_size; - p.data = (unsigned char *)data; - p.first_send_time = time_get(); - - if(flags&NETWORK_PACKETFLAG_VITAL) - { - /* save packet if we need to resend */ - NETPACKETDATA *resend = (NETPACKETDATA *)rb_alloc(&conn->buffer, sizeof(NETPACKETDATA)+data_size); - *resend = p; - resend->data = (unsigned char *)(resend+1); - mem_copy(resend->data, p.data, p.data_size); - } - - /* TODO: calc crc */ - conn_send_raw(conn, &p); -} - -static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr) -{ - if(conn->state != NETWORK_CONNSTATE_OFFLINE) - return -1; - - /* init connection */ - conn_reset(conn); - conn->peeraddr = *addr; - conn->token = current_token++; - mem_zero(conn->error_string, sizeof(conn->error_string)); - conn->state = NETWORK_CONNSTATE_CONNECT; - conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); - return 0; -} - -static void conn_disconnect(NETCONNECTION *conn, const char *reason) -{ - 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->error_string[0] = 0; - if(reason) - strcpy(conn->error_string, reason); - } - - conn_reset(conn); -} - -static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) -{ - conn->last_recv_time = time_get(); - conn->stats.recv_packets++; - conn->stats.recv_bytes += p->data_size + NETWORK_HEADER_SIZE; - - if(p->flags&NETWORK_PACKETFLAG_CLOSE) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn->remote_closed = 1; - - if(p->data_size) - conn_set_error(conn, (char *)p->data); - else - conn_set_error(conn, "no reason given"); - if(config.debug) - dbg_msg("conn", "closed reason='%s'", conn_error(conn)); - return 0; - } - - if(conn->state == NETWORK_CONNSTATE_OFFLINE) - { - if(p->flags == NETWORK_PACKETFLAG_CONNECT) - { - /* send response and init connection */ - conn->state = NETWORK_CONNSTATE_ONLINE; - conn->connected++; - conn->peeraddr = *addr; - conn->token = p->token; - conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); - if(config.debug) - dbg_msg("connection", "got connection, sending connect+accept"); - } - } - else if(net_addr4_cmp(&conn->peeraddr, addr) == 0) - { - if(p->token != conn->token) - return 0; - - if(conn->state == NETWORK_CONNSTATE_ONLINE) - { - /* remove packages that are acked */ - conn_ack(conn, p->ack); - - /* check if resend is requested */ - if(p->flags&NETWORK_PACKETFLAG_RESEND) - conn_resend(conn); - - if(p->flags&NETWORK_PACKETFLAG_VITAL) - { - if(p->seq == (conn->ack+1)%NETWORK_MAX_SEQACK) - { - /* in sequence */ - conn->ack = (conn->ack+1)%NETWORK_MAX_SEQACK; - } - else - { - /* out of sequence, request 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; - } - } - else - { - if(p->seq > conn->ack) - conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0); - } - - if(p->data_size == 0) - return 0; - - return 1; - } - else if(conn->state == NETWORK_CONNSTATE_CONNECT) - { - /* connection made */ - if(p->flags == (NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT)) - { - conn_send(conn, NETWORK_PACKETFLAG_ACCEPT, 0, 0); - conn->state = NETWORK_CONNSTATE_ONLINE; - conn->connected++; - dbg_msg("connection", "got connect+accept, sending accept. connection online"); - } - } - /* - else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) - { - // connection made - if(p->flags == NETWORK_PACKETFLAG_ACCEPT) - { - conn->state = NETWORK_CONNSTATE_ONLINE; - dbg_msg("connection", "got accept. connection online"); - } - }*/ - else - { - /* strange packet, wrong state */ - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "strange state and packet"); - } - } - else - { - /* strange packet, not ment for me */ - } - - return 0; -} - - - -static int conn_update(NETCONNECTION *conn) -{ - int64 now = time_get(); - - if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR) - return 0; - - /* watch out for major hitches */ - { - int64 delta = now-conn->last_update_time; - if(conn->last_update_time && delta > time_freq()/2) - { - RINGBUFFER_ITEM *item = conn->buffer.first; - - dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq())); - conn->last_recv_time += delta; - - while(item) - { - NETPACKETDATA *resend = (NETPACKETDATA *)rb_item_data(item); - resend->first_send_time += delta; - item = item->next; - } - } - - conn->last_update_time = now; - } - - - /* check for timeout */ - if(conn->state != NETWORK_CONNSTATE_OFFLINE && - conn->state != NETWORK_CONNSTATE_CONNECT && - (now-conn->last_recv_time) > time_freq()*10) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "timeout"); - } - - /* check for large buffer errors */ - if(conn->buffer.buffer_size > 1024*64) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (out of buffer)"); - } - - if(conn->buffer.first) - { - NETPACKETDATA *resend = (NETPACKETDATA *)(conn->buffer.first+1); - if(now-resend->first_send_time > time_freq()*10) - { - conn->state = NETWORK_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); - } - } - - /* send keep alives if nothing has happend for 250ms */ - if(conn->state == NETWORK_CONNSTATE_ONLINE) - { - if(time_get()-conn->last_send_time> time_freq()/4) - conn_send(conn, NETWORK_PACKETFLAG_VITAL, 0, 0); - } - else if(conn->state == NETWORK_CONNSTATE_CONNECT) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ - conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0); - } - else if(conn->state == NETWORK_CONNSTATE_CONNECTACCEPTED) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ - conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0); - } - - return 0; -} - - -static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet) -{ - /* check the size */ - if(size < NETWORK_HEADER_SIZE || size > NETWORK_MAX_PACKET_SIZE) - return -1; - - /* read the packet */ - packet->ID[0] = 'T'; - packet->ID[1] = 'W'; - packet->version = NETWORK_VERSION; - packet->flags = buffer[0]; - packet->seq = ((buffer[1]&0xf0)<<4)|buffer[2]; - packet->ack = ((buffer[1]&0x0f)<<8)|buffer[3]; - packet->crc = 0; - packet->token = (buffer[4]<<8)|buffer[5]; - packet->data_size = size - NETWORK_HEADER_SIZE; - packet->data = buffer+NETWORK_HEADER_SIZE; - - /* check the packet */ - if(packet->ID[0] != 'T' || packet->ID[1] != 'W') - return 1; - - if(packet->version != NETWORK_VERSION) - return 1; - - /* TODO: perform crc check */ - - /* return success */ - return 0; -} - -NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags) -{ - int i; - NETSERVER *server; - NETSOCKET socket = net_udp4_create(bindaddr); - if(socket == NETSOCKET_INVALID) - return 0; - - server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); - mem_zero(server, sizeof(NETSERVER)); - 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(i = 0; i < NETWORK_MAX_CLIENTS; i++) - conn_init(&server->slots[i].conn, server->socket); - - return server; -} - -int netserver_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 netserver_max_clients(NETSERVER *s) -{ - return s->max_clients; -} - -int netserver_close(NETSERVER *s) -{ - /* TODO: implement me */ - return 0; -} - -int netserver_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); - - return 0; -} - -int netserver_update(NETSERVER *s) -{ - int i; - for(i = 0; i < s->max_clients; i++) - { - conn_update(&s->slots[i].conn); - if(s->slots[i].conn.state == NETWORK_CONNSTATE_ERROR) - netserver_drop(s, i, conn_error(&s->slots[i].conn)); - } - return 0; -} - -int netserver_recv(NETSERVER *s, NETPACKET *packet) -{ - NETPACKETDATA data; - int i, r, bytes, found; - NETADDR4 addr; - - while(1) - { - bytes = net_udp4_recv(s->socket, &addr, s->recv_buffer, NETWORK_MAX_PACKET_SIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - r = check_packet(s->recv_buffer, bytes, &data); - if(r == 0) - { - if(data.flags&NETWORK_PACKETFLAG_CONNLESS) - { - /* connection less packets */ - packet->client_id = -1; - packet->address = addr; - packet->flags = PACKETFLAG_CONNLESS; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - else - { - /* ok packet, process it */ - if(data.flags == NETWORK_PACKETFLAG_CONNECT) - { - found = 0; - - /* check if we already got this client */ - for(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) - { - found = 1; /* silent ignore.. we got this client already */ - break; - } - } - - /* client that wants to connect */ - if(!found) - { - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE) - { - found = 1; - conn_feed(&s->slots[i].conn, &data, &addr); - if(s->new_client) - s->new_client(i, s->user_ptr); - break; - } - } - } - - if(!found) - { - /* 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(i = 0; i < s->max_clients; i++) - { - if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - if(conn_feed(&s->slots[i].conn, &data, &addr)) - { - if(data.data_size) - { - packet->client_id = i; - packet->address = addr; - packet->flags = 0; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - } - } - } - } - } - } - else - { - /* errornous packet, drop it */ - dbg_msg("server", "crazy packet"); - } - - /* read header */ - /* do checksum */ - } - - return 0; -} - -int netserver_send(NETSERVER *s, NETPACKET *packet) -{ - dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); - - if(packet->flags&PACKETFLAG_CONNLESS) - { - /* send connectionless packet */ - NETPACKETDATA p; - p.ID[0] = 'T'; - p.ID[1] = 'W'; - p.version = NETWORK_VERSION; - p.flags = NETWORK_PACKETFLAG_CONNLESS; - p.seq = 0; - p.ack = 0; - p.crc = 0; - p.data_size = packet->data_size; - p.data = (unsigned char *)packet->data; - send_packet(s->socket, &packet->address, &p); - } - else - { - int flags = 0; - dbg_assert(packet->client_id >= 0, "errornous client id"); - dbg_assert(packet->client_id < s->max_clients, "errornous client id"); - if(packet->flags&PACKETFLAG_VITAL) - flags |= NETWORK_PACKETFLAG_VITAL; - conn_send(&s->slots[packet->client_id].conn, flags, packet->data_size, packet->data); - } - return 0; -} - -void netserver_stats(NETSERVER *s, NETSTATS *stats) -{ - int num_stats = sizeof(NETSTATS)/sizeof(int); - int *istats = (int *)stats; - int c, i; - - mem_zero(stats, sizeof(NETSTATS)); - - for(c = 0; c < s->max_clients; c++) - { - if(s->slots[c].conn.state != NETWORK_CONNSTATE_OFFLINE) - { - int *sstats = (int *)(&(s->slots[c].conn.stats)); - for(i = 0; i < num_stats; i++) - istats[i] += sstats[i]; - } - } -} - -NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags) -{ - NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); - mem_zero(client, sizeof(NETCLIENT)); - client->socket = net_udp4_create(bindaddr); - conn_init(&client->conn, client->socket); - return client; -} - -int netclient_close(NETCLIENT *c) -{ - /* TODO: implement me */ - return 0; -} - -int netclient_update(NETCLIENT *c) -{ - conn_update(&c->conn); - if(c->conn.state == NETWORK_CONNSTATE_ERROR) - netclient_disconnect(c, conn_error(&c->conn)); - return 0; -} - -int netclient_disconnect(NETCLIENT *c, const char *reason) -{ - dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); - conn_disconnect(&c->conn, reason); - return 0; -} - -int netclient_connect(NETCLIENT *c, NETADDR4 *addr) -{ - conn_connect(&c->conn, addr); - return 0; -} - -int netclient_recv(NETCLIENT *c, NETPACKET *packet) -{ - while(1) - { - NETADDR4 addr; - NETPACKETDATA data; - int r; - int bytes = net_udp4_recv(c->socket, &addr, c->recv_buffer, NETWORK_MAX_PACKET_SIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - r = check_packet(c->recv_buffer, bytes, &data); - - if(r == 0) - { - if(data.flags&NETWORK_PACKETFLAG_CONNLESS) - { - /* connection less packets */ - packet->client_id = -1; - packet->address = addr; - packet->flags = PACKETFLAG_CONNLESS; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - else - { - if(conn_feed(&c->conn, &data, &addr)) - { - /* fill in packet */ - packet->client_id = 0; - packet->address = addr; - packet->flags = 0; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - else - { - /* errornous packet, drop it */ - } - } - } - } - - return 0; -} - -int netclient_send(NETCLIENT *c, NETPACKET *packet) -{ - dbg_assert(packet->data_size < NETWORK_MAX_PAYLOAD, "packet payload too big"); - - if(packet->flags&PACKETFLAG_CONNLESS) - { - /* send connectionless packet */ - NETPACKETDATA p; - p.ID[0] = 'T'; - p.ID[1] = 'W'; - p.version = NETWORK_VERSION; - p.flags = NETWORK_PACKETFLAG_CONNLESS; - 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); - } - else - { - int flags = 0; - dbg_assert(packet->client_id == 0, "errornous client id"); - if(packet->flags&PACKETFLAG_VITAL) - flags |= NETWORK_PACKETFLAG_VITAL; - conn_send(&c->conn, flags, packet->data_size, packet->data); - } - return 0; -} - -int netclient_state(NETCLIENT *c) -{ - if(c->conn.state == NETWORK_CONNSTATE_ONLINE) - return NETSTATE_ONLINE; - if(c->conn.state == NETWORK_CONNSTATE_OFFLINE) - return NETSTATE_OFFLINE; - return NETSTATE_CONNECTING; -} - -int netclient_gotproblems(NETCLIENT *c) -{ - if(time_get() - c->conn.last_recv_time > time_freq()) - return 1; - return 0; -} - -void netclient_stats(NETCLIENT *c, NETSTATS *stats) -{ - *stats = c->conn.stats; -} - -const char *netclient_error_string(NETCLIENT *c) -{ - return conn_error(&c->conn); -} diff --git a/src/engine/network.h b/src/engine/network.h deleted file mode 100644 index 338c5982..00000000 --- a/src/engine/network.h +++ /dev/null @@ -1,113 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -typedef struct -{ - /* -1 means that it's a stateless packet */ - /* 0 on the client means the server */ - int client_id; - NETADDR4 address; /* only used when client_id == -1 */ - int flags; - int data_size; - const void *data; -} NETPACKET; - -typedef struct -{ - int send_bytes; - int recv_bytes; - int send_packets; - int recv_packets; - - int resend_packets; - int resend_bytes; -} NETSTATS; - -typedef struct NETSERVER_t NETSERVER; -typedef struct NETCLIENT_t NETCLIENT; - -enum -{ - NETFLAG_ALLOWSTATELESS=1, - PACKETFLAG_VITAL=1, - PACKETFLAG_CONNLESS=2, - - NETSTATE_OFFLINE=0, - NETSTATE_CONNECTING, - NETSTATE_ONLINE -}; - -typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); -typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); - -/* server side */ -NETSERVER *netserver_open(NETADDR4 bindaddr, int max_clients, int flags); -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user); -int netserver_recv(NETSERVER *s, NETPACKET *packet); -int netserver_send(NETSERVER *s, NETPACKET *packet); -int netserver_close(NETSERVER *s); -int netserver_update(NETSERVER *s); -int netserver_drop(NETSERVER *s, int client_id, const char *reason); -int netserver_max_clients(NETSERVER *s); -void netserver_stats(NETSERVER *s, NETSTATS *stats); - -/* client side */ -NETCLIENT *netclient_open(NETADDR4 bindaddr, int flags); -int netclient_disconnect(NETCLIENT *c, const char *reason); -int netclient_connect(NETCLIENT *c, NETADDR4 *addr); -int netclient_recv(NETCLIENT *c, NETPACKET *packet); -int netclient_send(NETCLIENT *c, NETPACKET *packet); -int netclient_close(NETCLIENT *c); -int netclient_update(NETCLIENT *c); -int netclient_state(NETCLIENT *c); -int netclient_gotproblems(NETCLIENT *c); -void netclient_stats(NETCLIENT *c, NETSTATS *stats); -const char *netclient_error_string(NETCLIENT *c); - -#ifdef __cplusplus -class net_server -{ - NETSERVER *ptr; -public: - net_server() : ptr(0) {} - ~net_server() { close(); } - - int open(NETADDR4 bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; } - int close() { int r = netserver_close(ptr); ptr = 0; return r; } - - int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) - { return netserver_set_callbacks(ptr, new_client, del_client, user); } - - int recv(NETPACKET *packet) { return netserver_recv(ptr, packet); } - int send(NETPACKET *packet) { return netserver_send(ptr, packet); } - int update() { return netserver_update(ptr); } - - int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); } - - int max_clients() { return netserver_max_clients(ptr); } - void stats(NETSTATS *stats) { netserver_stats(ptr, stats); } -}; - - -class net_client -{ - NETCLIENT *ptr; -public: - net_client() : ptr(0) {} - ~net_client() { close(); } - - int open(NETADDR4 bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; } - int close() { int r = netclient_close(ptr); ptr = 0; return r; } - - int connect(NETADDR4 *addr) { return netclient_connect(ptr, addr); } - int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); } - - int recv(NETPACKET *packet) { return netclient_recv(ptr, packet); } - int send(NETPACKET *packet) { return netclient_send(ptr, packet); } - int update() { return netclient_update(ptr); } - - const char *error_string() { return netclient_error_string(ptr); } - - int state() { return netclient_state(ptr); } - void stats(NETSTATS *stats) { netclient_stats(ptr, stats); } -}; -#endif diff --git a/src/engine/packer.c b/src/engine/packer.c deleted file mode 100644 index 30cb6755..00000000 --- a/src/engine/packer.c +++ /dev/null @@ -1,91 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include "packer.h" -#include "compression.h" - -void packer_reset(PACKER *p) -{ - p->error = 0; - p->current = p->buffer; - p->end = p->current + PACKER_BUFFER_SIZE; -} - -void packer_add_int(PACKER *p, int i) -{ - p->current = vint_pack(p->current, i); -} - -void packer_add_string(PACKER *p, const char *str, int limit) -{ - if(limit > 0) - { - while(*str && limit != 0) - { - *p->current++ = *str++; - limit--; - } - *p->current++ = 0; - } - else - { - while(*str) - *p->current++ = *str++; - *p->current++ = 0; - } -} - -void packer_add_raw(PACKER *p, const unsigned char *data, int size) -{ - while(size) - { - *p->current++ = *data++; - size--; - } -} - -int packer_size(PACKER *p) -{ - return (const unsigned char *)p->current-(const unsigned char *)p->buffer; -} - -const unsigned char *packer_data(PACKER *p) -{ - return (const unsigned char *)p->buffer; -} - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size) -{ - p->error = 0; - p->start = data; - p->end = p->start + size; - p->current = p->start; -} - -int unpacker_get_int(UNPACKER *p) -{ - int i; - if(p->current >= p->end) - return 0; - p->current = vint_unpack(p->current, &i); - return i; -} - -const char *unpacker_get_string(UNPACKER *p) -{ - const char *ptr; - if(p->current >= p->end) - return ""; - - ptr = (const char *)p->current; - while(*p->current) /* skip the string */ - p->current++; - p->current++; - return ptr; -} - -const unsigned char *unpacker_get_raw(UNPACKER *p, int size) -{ - const unsigned char *ptr = p->current; - p->current += size; - return ptr; -} diff --git a/src/engine/packer.h b/src/engine/packer.h deleted file mode 100644 index 71b99ff7..00000000 --- a/src/engine/packer.h +++ /dev/null @@ -1,35 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -enum -{ - PACKER_BUFFER_SIZE=1024*2 -}; - -typedef struct -{ - - unsigned char buffer[PACKER_BUFFER_SIZE]; - unsigned char *current; - unsigned char *end; - int error; -} PACKER; - -typedef struct -{ - const unsigned char *current; - const unsigned char *start; - const unsigned char *end; - int error; -} UNPACKER; - -void packer_reset(PACKER *p); -void packer_add_int(PACKER *p, int i); -void packer_add_string(PACKER *p, const char *str, int limit); -void packer_add_raw(PACKER *p, const unsigned char *data, int size); -int packer_size(PACKER *p); -const unsigned char *packer_data(PACKER *p); - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size); -int unpacker_get_int(UNPACKER *p); -const char *unpacker_get_string(UNPACKER *p); -const unsigned char *unpacker_get_raw(UNPACKER *p, int size); diff --git a/src/engine/protocol.h b/src/engine/protocol.h deleted file mode 100644 index e85ecf9f..00000000 --- a/src/engine/protocol.h +++ /dev/null @@ -1,60 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "system.h" - -/* - Connection diagram - How the initilization works. - - Client -> INFO -> Server - Contains version info, name, and some other info. - - Client <- MAP <- Server - Contains current map. - - Client -> READY -> Server - The client has loaded the map and is ready to go, - but the mod needs to send it's information aswell. - modc_connected is called on the client and - mods_connected is called on the server. - The client should call client_entergame when the - mod has done it's initilization. - - Client -> ENTERGAME -> Server - Tells the server to start sending snapshots. - client_entergame and server_client_enter is called. -*/ - - -enum -{ - NETMSG_NULL=0, - - /* the first thing sent by the client - contains the version info for the client */ - NETMSG_INFO=1, - - /* sent by server */ - NETMSG_MAP, - NETMSG_SNAP, - NETMSG_SNAPEMPTY, - NETMSG_SNAPSINGLE, - NETMSG_SNAPSMALL, - - /* sent by client */ - NETMSG_READY, - NETMSG_ENTERGAME, - NETMSG_INPUT, - NETMSG_CMD, - - /* sent by both */ - NETMSG_ERROR -}; - - -/* this should be revised */ -enum -{ - MAX_NAME_LENGTH=32, - MAX_CLANNAME_LENGTH=32, - MAX_INPUT_SIZE=128, - MAX_SNAPSHOT_PACKSIZE=900 -}; diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c new file mode 100644 index 00000000..00100b10 --- /dev/null +++ b/src/engine/server/es_server.c @@ -0,0 +1,901 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +#include + +static SNAPBUILD builder; + +static int64 game_start_time; +static int current_tick = 0; + +static int browseinfo_gametype = -1; +static int browseinfo_progression = -1; + +static int64 lastheartbeat; +static NETADDR4 master_server; + +static char current_map[64]; +static int current_map_crc; + +void *snap_new_item(int type, int id, int size) +{ + dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); + dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); + return snapbuild_new_item(&builder, type, id, size); +} + +typedef struct +{ + short next; + short state; /* 0 = free, 1 = alloced, 2 = timed */ + int timeout; +} SNAP_ID; + +static const int MAX_IDS = 16*1024; /* should be lowered */ +static SNAP_ID snap_ids[16*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; + + +enum +{ + SRVCLIENT_STATE_EMPTY = 0, + SRVCLIENT_STATE_CONNECTING, + SRVCLIENT_STATE_READY, + SRVCLIENT_STATE_INGAME +}; + +typedef struct +{ + int data[MAX_INPUT_SIZE]; + int pred_tick; /* tick that the client predicted for the input */ + int game_tick; /* the tick that was chosen for the input */ + int64 timeleft; /* how much time in ms there were left before this should be applied */ +} CLIENT_INPUT; + +/* */ +typedef struct +{ + /* connection state info */ + int state; + int latency; + + int last_acked_snapshot; + SNAPSTORAGE snapshots; + + CLIENT_INPUT inputs[200]; /* TODO: handle input better */ + int current_input; + + char name[MAX_NAME_LENGTH]; + char clan[MAX_CLANNAME_LENGTH]; +} CLIENT; + +static CLIENT clients[MAX_CLIENTS]; +static NETSERVER *net; + +static void snap_init_id() +{ + int i; + for(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; +} + +static void snap_remove_first_timeout() +{ + int next_timed = snap_ids[snap_first_timed_id].next; + + /* add it to the free list */ + snap_ids[snap_first_timed_id].next = snap_first_free_id; + snap_ids[snap_first_timed_id].state = 0; + snap_first_free_id = snap_first_timed_id; + + /* remove it from the timed list */ + snap_first_timed_id = next_timed; + if(snap_first_timed_id == -1) + snap_last_timed_id = -1; + + snap_id_usage--; +} + +int snap_new_id() +{ + int id; + int64 now = time_get(); + dbg_assert(snap_id_inited == 1, "requesting id too soon"); + + + /* process timed ids */ + while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) + snap_remove_first_timeout(); + + id = snap_first_free_id; + dbg_assert(id != -1, "id error"); + snap_first_free_id = snap_ids[snap_first_free_id].next; + snap_ids[id].state = 1; + snap_id_usage++; + snap_id_inusage++; + return id; +} + +void snap_timeout_ids() +{ + /* process timed ids */ + while(snap_first_timed_id != -1) + snap_remove_first_timeout(); +} + +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 = time_get()+time_freq()*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; + } +} + +const char *server_clientname(int client_id) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) + return "(invalid client)"; + return clients[client_id].name; +} + +void server_setclientname(int client_id, const char *name) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) + return; + strncpy(clients[client_id].name, name, MAX_NAME_LENGTH); +} + +void server_setbrowseinfo(int game_type, int progression) +{ + browseinfo_gametype = game_type; + browseinfo_progression = progression; +} + +int server_tick() +{ + return current_tick; +} + +int64 server_tick_start_time(int tick) +{ + return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED; +} + +int server_tickspeed() +{ + return SERVER_TICK_SPEED; +} + +int server_init() +{ + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + clients[i].state = SRVCLIENT_STATE_EMPTY; + clients[i].name[0] = 0; + clients[i].clan[0] = 0; + snapstorage_init(&clients[i].snapshots); + } + + current_tick = 0; + + return 0; +} + +int server_getclientinfo(int client_id, CLIENT_INFO *info) +{ + dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); + dbg_assert(info != 0, "info can not be null"); + + if(clients[client_id].state == SRVCLIENT_STATE_INGAME) + { + info->name = clients[client_id].name; + info->latency = clients[client_id].latency; + return 1; + } + return 0; +} + + +int server_send_msg(int client_id) +{ + const MSG_INFO *info = msg_get_info(); + NETPACKET packet; + mem_zero(&packet, sizeof(NETPACKET)); + + packet.client_id = client_id; + packet.data = info->data; + packet.data_size = info->size; + + if(info->flags&MSGFLAG_VITAL) + packet.flags = PACKETFLAG_VITAL; + + 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); + return 0; +} + +static void server_do_snap() +{ + int i, k; + mods_presnap(); + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state == SRVCLIENT_STATE_INGAME) + { + char data[MAX_SNAPSHOT_SIZE]; + char deltadata[MAX_SNAPSHOT_SIZE]; + char compdata[MAX_SNAPSHOT_SIZE]; + int snapshot_size; + int crc; + static SNAPSHOT emptysnap; + SNAPSHOT *deltashot = &emptysnap; + int deltashot_size; + int delta_tick = -1; + int input_predtick = -1; + int64 timeleft = 0; + int deltasize; + + snapbuild_init(&builder); + mods_snap(i); + + /* finish snapshot */ + snapshot_size = snapbuild_finish(&builder, data); + crc = snapshot_crc((SNAPSHOT*)data); + + /* remove old snapshos */ + /* keep 1 seconds worth of snapshots */ + snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED); + + /* save it the snapshot */ + snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data); + + /* find snapshot that we can preform delta against */ + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + { + deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot); + if(deltashot_size >= 0) + delta_tick = clients[i].last_acked_snapshot; + } + + for(k = 0; k < 200; k++) /* TODO: do this better */ + { + if(clients[i].inputs[k].game_tick == current_tick) + { + timeleft = clients[i].inputs[k].timeleft; + input_predtick = clients[i].inputs[k].pred_tick; + break; + } + } + + /* create delta */ + deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata); + + if(deltasize) + { + /* compress it */ + unsigned char intdata[MAX_SNAPSHOT_SIZE]; + int intsize = intpack_compress(deltadata, deltasize, intdata); + int snapshot_size = zerobit_compress(intdata, intsize, compdata); + const int max_size = MAX_SNAPSHOT_PACKSIZE; + int numpackets = (snapshot_size+max_size-1)/max_size; + int n, left; + + for(n = 0, left = snapshot_size; left; n++) + { + int chunk = left < max_size ? left : max_size; + left -= chunk; + + if(numpackets == 1) + msg_pack_start_system(NETMSG_SNAPSINGLE, 0); + else + msg_pack_start_system(NETMSG_SNAP, 0); + + msg_pack_int(current_tick); + msg_pack_int(current_tick-delta_tick); /* compressed with */ + msg_pack_int(input_predtick); + msg_pack_int((timeleft*1000)/time_freq()); + + if(numpackets != 1) + { + msg_pack_int(numpackets); + msg_pack_int(n); + } + + msg_pack_int(crc); + msg_pack_int(chunk); + msg_pack_raw(&compdata[n*max_size], chunk); + msg_pack_end(); + server_send_msg(i); + } + } + else + { + msg_pack_start_system(NETMSG_SNAPEMPTY, 0); + msg_pack_int(current_tick); + msg_pack_int(current_tick-delta_tick); /* compressed with */ + msg_pack_int(input_predtick); + msg_pack_int((timeleft*1000)/time_freq()); + msg_pack_end(); + server_send_msg(i); + } + } + } + + mods_postsnap(); +} + + +static int new_client_callback(int cid, void *user) +{ + int i; + clients[cid].state = SRVCLIENT_STATE_CONNECTING; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + + /* reset input */ + for(i = 0; i < 200; i++) + { + clients[cid].inputs[i].game_tick = -1; + clients[cid].inputs[i].pred_tick = -1; + } + clients[cid].current_input = 0; + + snapstorage_purge_all(&clients[cid].snapshots); + clients[cid].last_acked_snapshot = -1; + return 0; +} + +static int del_client_callback(int cid, void *user) +{ + /* notify the mod about the drop */ + if(clients[cid].state == SRVCLIENT_STATE_READY || + clients[cid].state == SRVCLIENT_STATE_INGAME) + { + mods_client_drop(cid); + } + + clients[cid].state = SRVCLIENT_STATE_EMPTY; + clients[cid].name[0] = 0; + clients[cid].clan[0] = 0; + snapstorage_purge_all(&clients[cid].snapshots); + return 0; +} + + +static void server_send_map(int cid) +{ + msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL); + msg_pack_string(config.sv_map, 0); + msg_pack_int(current_map_crc); + msg_pack_end(); + server_send_msg(cid); +} + +static void server_send_heartbeat() +{ + static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; + unsigned short port = config.sv_port; + NETPACKET packet; + + mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); + + packet.client_id = -1; + packet.address = master_server; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2; + packet.data = &data; + + /* supply the set port that the master can use if it has problems */ + if(config.sv_external_port) + port = config.sv_external_port; + data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; + data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; + + netserver_send(net, &packet); +} + +static void server_process_client_packet(NETPACKET *packet) +{ + int cid = packet->client_id; + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + if(sys) + { + /* system message */ + if(msg == NETMSG_INFO) + { + char version[64]; + const char *password; + strncpy(version, msg_unpack_string(), 64); + if(strcmp(version, mods_net_version()) != 0) + { + /* OH FUCK! wrong version, drop him */ + char reason[256]; + sprintf(reason, "wrong version. server is running %s.", mods_net_version()); + netserver_drop(net, cid, reason); + return; + } + + strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); + strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); + password = msg_unpack_string(); + + if(config.password[0] != 0 && strcmp(config.password, password) != 0) + { + /* wrong password */ + netserver_drop(net, cid, "wrong password"); + return; + } + + server_send_map(cid); + } + else if(msg == NETMSG_READY) + { + if(clients[cid].state == SRVCLIENT_STATE_CONNECTING) + { + dbg_msg("server", "player is ready. cid=%x", cid); + clients[cid].state = SRVCLIENT_STATE_READY; + mods_connected(cid); + } + } + else if(msg == NETMSG_ENTERGAME) + { + if(clients[cid].state != SRVCLIENT_STATE_INGAME) + { + dbg_msg("server", "player as entered the game. cid=%x", cid); + clients[cid].state = SRVCLIENT_STATE_INGAME; + mods_client_enter(cid); + } + } + else if(msg == NETMSG_INPUT) + { + int tick, size, i; + CLIENT_INPUT *input; + int64 tagtime; + + clients[cid].last_acked_snapshot = msg_unpack_int(); + if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0) >= 0) + clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); + + tick = msg_unpack_int(); + size = msg_unpack_int(); + + input = &clients[cid].inputs[clients[cid].current_input]; + input->timeleft = server_tick_start_time(tick)-time_get(); + input->pred_tick = tick; + + if(tick <= server_tick()) + { + /* TODO: how should we handle this */ + dbg_msg("server", "input got in late for=%d cur=%d", tick, server_tick()); + tick = server_tick()+1; + } + + input->game_tick = tick; + + for(i = 0; i < size/4; i++) + input->data[i] = msg_unpack_int(); + + clients[cid].current_input++; + clients[cid].current_input %= 200; + } + else if(msg == NETMSG_CMD) + { + const char *pw = msg_unpack_string(); + const char *cmd = msg_unpack_string(); + if(config.rcon_password[0] != 0 && strcmp(pw, config.rcon_password) == 0) + { + dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); + config_set(cmd); + } + } + else + { + dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); + } + } + else + { + /* game message */ + mods_message(msg, cid); + } +} + +static void server_send_serverinfo(NETADDR4 *addr) +{ + NETPACKET packet; + PACKER p; + char buf[128]; + + /* count the players */ + int c = 0; + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state != SRVCLIENT_STATE_EMPTY) + c++; + } + + packer_reset(&p); + packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + packer_add_string(&p, mods_net_version(), 32); + packer_add_string(&p, config.sv_name, 64); + packer_add_string(&p, config.sv_map, 32); + + /* gametype */ + sprintf(buf, "%d", browseinfo_gametype); + packer_add_string(&p, buf, 2); + + /* flags */ + i = 0; + if(strlen(config.password)) + i |= 1; + sprintf(buf, "%d", i); + packer_add_string(&p, buf, 2); + + /* progression */ + sprintf(buf, "%d", browseinfo_progression); + packer_add_string(&p, buf, 4); + + sprintf(buf, "%d", c); packer_add_string(&p, buf, 3); /* num players */ + sprintf(buf, "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(clients[i].state != SRVCLIENT_STATE_EMPTY) + { + packer_add_string(&p, clients[i].name, 48); /* player name */ + packer_add_string(&p, "0", 6); /* score */ + } + } + + + packet.client_id = -1; + packet.address = *addr; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = packer_size(&p); + packet.data = packer_data(&p); + netserver_send(net, &packet); +} + + +static void server_send_fwcheckresponse(NETADDR4 *addr) +{ + NETPACKET packet; + packet.client_id = -1; + packet.address = *addr; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE); + packet.data = SERVERBROWSE_FWRESPONSE; + netserver_send(net, &packet); +} + +static void server_pump_network() +{ + NETPACKET packet; + + netserver_update(net); + + /* process packets */ + while(netserver_recv(net, &packet)) + { + if(packet.client_id == -1) + { + /* stateless */ + if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) && + memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + server_send_serverinfo(&packet.address); + } + else if(packet.data_size == sizeof(SERVERBROWSE_FWCHECK) && + memcmp(packet.data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) + { + server_send_fwcheckresponse(&packet.address); + } + else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) && + memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) + { + 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) + { + dbg_msg("server", "ERROR: the master server reports that clients can not connect to this server."); + dbg_msg("server", "ERROR: configure your firewall/nat to let trough udp on port %d.", config.sv_port); + } + } + else + server_process_client_packet(&packet); + } +} + +static int server_load_map(const char *mapname) +{ + DATAFILE *df; + char buf[512]; + sprintf(buf, "data/maps/%s.map", mapname); + df = datafile_load(buf); + if(!df) + return 0; + + /* reinit snapshot ids */ + snap_timeout_ids(); + + /* get the crc of the map */ + current_map_crc = datafile_crc(buf); + dbg_msg("server", "%s crc is %08x", buf, current_map_crc); + + strcpy(current_map, mapname); + map_set(df); + return 1; +} + + +static int server_run() +{ + NETADDR4 bindaddr; + + net_init(); + + snap_init_id(); + + /* load map */ + if(!server_load_map(config.sv_map)) + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + return -1; + } + + /* start server */ + if(strlen(config.sv_bindaddr) && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) != 0) + { + /* sweet! */ + } + else + { + mem_zero(&bindaddr, sizeof(bindaddr)); + bindaddr.port = config.sv_port; + } + + net = netserver_open(bindaddr, config.sv_max_clients, 0); + if(!net) + { + dbg_msg("server", "couldn't open socket. port might already be in use"); + return -1; + } + + netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); + + dbg_msg("server", "server name is '%s'", config.sv_name); + dbg_msg("server", "masterserver is '%s'", config.masterserver); + if(net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0) + { + /* TODO: fix me */ + /*master_server = netaddr4(0, 0, 0, 0, 0); */ + } + + mods_init(); + dbg_msg("server", "version %s", mods_net_version()); + + /* start game */ + { + int64 time_per_heartbeat = time_freq() * 30; + int64 reporttime = time_get(); + int reportinterval = 3; + + int64 simulationtime = 0; + int64 snaptime = 0; + int64 networktime = 0; + int64 totaltime = 0; + + lastheartbeat = 0; + game_start_time = time_get(); + + if(config.debug) + dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024); + + while(1) + { + int64 t = time_get(); + /* load new map TODO: don't poll this */ + if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) + { + config.sv_map_reload = 0; + + /* load map */ + if(server_load_map(config.sv_map)) + { + int c; + + /* new map loaded */ + mods_shutdown(); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(clients[c].state == SRVCLIENT_STATE_EMPTY) + continue; + + server_send_map(c); + clients[c].state = SRVCLIENT_STATE_CONNECTING; + clients[c].last_acked_snapshot = -1; + snapstorage_purge_all(&clients[c].snapshots); + } + + game_start_time = time_get(); + current_tick = 0; + mods_init(); + } + else + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + config_set_sv_map(&config, current_map); + } + } + + if(t > server_tick_start_time(current_tick+1)) + { + current_tick++; + + /* apply new input */ + { + int c, i; + for(c = 0; c < MAX_CLIENTS; c++) + { + if(clients[c].state == SRVCLIENT_STATE_EMPTY) + continue; + for(i = 0; i < 200; i++) + { + if(clients[c].inputs[i].game_tick == server_tick()) + { + mods_client_input(c, clients[c].inputs[i].data); + break; + } + } + } + } + + /* progress game */ + { + int64 start = time_get(); + mods_tick(); + simulationtime += time_get()-start; + } + + /* snap game */ + if(config.sv_bandwidth_mode == 0 || + (config.sv_bandwidth_mode == 1 && current_tick%2) || + (config.sv_bandwidth_mode == 2 && (current_tick%3) == 0 )) + /* if(current_tick&1) */ + { + int64 start = time_get(); + server_do_snap(); + snaptime += time_get()-start; + } + } + + if(config.sv_sendheartbeats) + { + if (t > lastheartbeat+time_per_heartbeat) + { + server_send_heartbeat(); + lastheartbeat = t+time_per_heartbeat; + } + } + + { + int64 start = time_get(); + server_pump_network(); + networktime += time_get()-start; + } + + if(reporttime < time_get()) + { + if(config.debug) + { + static NETSTATS prev_stats; + NETSTATS stats; + netserver_stats(net, &stats); + dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms tot=%.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); + + dbg_msg("server", "send=%8d recv=%8d", + (stats.send_bytes - prev_stats.send_bytes)/reportinterval, + (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); + + prev_stats = stats; + } + + simulationtime = 0; + snaptime = 0; + networktime = 0; + totaltime = 0; + + reporttime += time_freq()*reportinterval; + } + totaltime += time_get()-t; + thread_sleep(1); + } + } + + mods_shutdown(); + map_unload(); + + return 0; +} + + +int main(int argc, char **argv) +{ + dbg_msg("server", "starting..."); + engine_init("Teewars", argc, argv); + server_run(); + return 0; +} + diff --git a/src/engine/server/server.c b/src/engine/server/server.c deleted file mode 100644 index d2ef39ed..00000000 --- a/src/engine/server/server.c +++ /dev/null @@ -1,901 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - -#include - -static SNAPBUILD builder; - -static int64 game_start_time; -static int current_tick = 0; - -static int browseinfo_gametype = -1; -static int browseinfo_progression = -1; - -static int64 lastheartbeat; -static NETADDR4 master_server; - -static char current_map[64]; -static int current_map_crc; - -void *snap_new_item(int type, int id, int size) -{ - dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); - dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); - return snapbuild_new_item(&builder, type, id, size); -} - -typedef struct -{ - short next; - short state; /* 0 = free, 1 = alloced, 2 = timed */ - int timeout; -} SNAP_ID; - -static const int MAX_IDS = 16*1024; /* should be lowered */ -static SNAP_ID snap_ids[16*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; - - -enum -{ - SRVCLIENT_STATE_EMPTY = 0, - SRVCLIENT_STATE_CONNECTING, - SRVCLIENT_STATE_READY, - SRVCLIENT_STATE_INGAME -}; - -typedef struct -{ - int data[MAX_INPUT_SIZE]; - int pred_tick; /* tick that the client predicted for the input */ - int game_tick; /* the tick that was chosen for the input */ - int64 timeleft; /* how much time in ms there were left before this should be applied */ -} CLIENT_INPUT; - -/* */ -typedef struct -{ - /* connection state info */ - int state; - int latency; - - int last_acked_snapshot; - SNAPSTORAGE snapshots; - - CLIENT_INPUT inputs[200]; /* TODO: handle input better */ - int current_input; - - char name[MAX_NAME_LENGTH]; - char clan[MAX_CLANNAME_LENGTH]; -} CLIENT; - -static CLIENT clients[MAX_CLIENTS]; -static NETSERVER *net; - -static void snap_init_id() -{ - int i; - for(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; -} - -static void snap_remove_first_timeout() -{ - int next_timed = snap_ids[snap_first_timed_id].next; - - /* add it to the free list */ - snap_ids[snap_first_timed_id].next = snap_first_free_id; - snap_ids[snap_first_timed_id].state = 0; - snap_first_free_id = snap_first_timed_id; - - /* remove it from the timed list */ - snap_first_timed_id = next_timed; - if(snap_first_timed_id == -1) - snap_last_timed_id = -1; - - snap_id_usage--; -} - -int snap_new_id() -{ - int id; - int64 now = time_get(); - dbg_assert(snap_id_inited == 1, "requesting id too soon"); - - - /* process timed ids */ - while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) - snap_remove_first_timeout(); - - id = snap_first_free_id; - dbg_assert(id != -1, "id error"); - snap_first_free_id = snap_ids[snap_first_free_id].next; - snap_ids[id].state = 1; - snap_id_usage++; - snap_id_inusage++; - return id; -} - -void snap_timeout_ids() -{ - /* process timed ids */ - while(snap_first_timed_id != -1) - snap_remove_first_timeout(); -} - -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 = time_get()+time_freq()*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; - } -} - -const char *server_clientname(int client_id) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return "(invalid client)"; - return clients[client_id].name; -} - -void server_setclientname(int client_id, const char *name) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return; - strncpy(clients[client_id].name, name, MAX_NAME_LENGTH); -} - -void server_setbrowseinfo(int game_type, int progression) -{ - browseinfo_gametype = game_type; - browseinfo_progression = progression; -} - -int server_tick() -{ - return current_tick; -} - -int64 server_tick_start_time(int tick) -{ - return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED; -} - -int server_tickspeed() -{ - return SERVER_TICK_SPEED; -} - -int server_init() -{ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - clients[i].state = SRVCLIENT_STATE_EMPTY; - clients[i].name[0] = 0; - clients[i].clan[0] = 0; - snapstorage_init(&clients[i].snapshots); - } - - current_tick = 0; - - return 0; -} - -int server_getclientinfo(int client_id, CLIENT_INFO *info) -{ - dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); - dbg_assert(info != 0, "info can not be null"); - - if(clients[client_id].state == SRVCLIENT_STATE_INGAME) - { - info->name = clients[client_id].name; - info->latency = clients[client_id].latency; - return 1; - } - return 0; -} - - -int server_send_msg(int client_id) -{ - const MSG_INFO *info = msg_get_info(); - NETPACKET packet; - mem_zero(&packet, sizeof(NETPACKET)); - - packet.client_id = client_id; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags = PACKETFLAG_VITAL; - - 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); - return 0; -} - -static void server_do_snap() -{ - int i, k; - mods_presnap(); - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - char data[MAX_SNAPSHOT_SIZE]; - char deltadata[MAX_SNAPSHOT_SIZE]; - char compdata[MAX_SNAPSHOT_SIZE]; - int snapshot_size; - int crc; - static SNAPSHOT emptysnap; - SNAPSHOT *deltashot = &emptysnap; - int deltashot_size; - int delta_tick = -1; - int input_predtick = -1; - int64 timeleft = 0; - int deltasize; - - snapbuild_init(&builder); - mods_snap(i); - - /* finish snapshot */ - snapshot_size = snapbuild_finish(&builder, data); - crc = snapshot_crc((SNAPSHOT*)data); - - /* remove old snapshos */ - /* keep 1 seconds worth of snapshots */ - snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED); - - /* save it the snapshot */ - snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data); - - /* find snapshot that we can preform delta against */ - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - { - deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot); - if(deltashot_size >= 0) - delta_tick = clients[i].last_acked_snapshot; - } - - for(k = 0; k < 200; k++) /* TODO: do this better */ - { - if(clients[i].inputs[k].game_tick == current_tick) - { - timeleft = clients[i].inputs[k].timeleft; - input_predtick = clients[i].inputs[k].pred_tick; - break; - } - } - - /* create delta */ - deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata); - - if(deltasize) - { - /* compress it */ - unsigned char intdata[MAX_SNAPSHOT_SIZE]; - int intsize = intpack_compress(deltadata, deltasize, intdata); - int snapshot_size = zerobit_compress(intdata, intsize, compdata); - const int max_size = MAX_SNAPSHOT_PACKSIZE; - int numpackets = (snapshot_size+max_size-1)/max_size; - int n, left; - - for(n = 0, left = snapshot_size; left; n++) - { - int chunk = left < max_size ? left : max_size; - left -= chunk; - - if(numpackets == 1) - msg_pack_start_system(NETMSG_SNAPSINGLE, 0); - else - msg_pack_start_system(NETMSG_SNAP, 0); - - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - msg_pack_int(input_predtick); - msg_pack_int((timeleft*1000)/time_freq()); - - if(numpackets != 1) - { - msg_pack_int(numpackets); - msg_pack_int(n); - } - - msg_pack_int(crc); - msg_pack_int(chunk); - msg_pack_raw(&compdata[n*max_size], chunk); - msg_pack_end(); - server_send_msg(i); - } - } - else - { - msg_pack_start_system(NETMSG_SNAPEMPTY, 0); - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - msg_pack_int(input_predtick); - msg_pack_int((timeleft*1000)/time_freq()); - msg_pack_end(); - server_send_msg(i); - } - } - } - - mods_postsnap(); -} - - -static int new_client_callback(int cid, void *user) -{ - int i; - clients[cid].state = SRVCLIENT_STATE_CONNECTING; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - - /* reset input */ - for(i = 0; i < 200; i++) - { - clients[cid].inputs[i].game_tick = -1; - clients[cid].inputs[i].pred_tick = -1; - } - clients[cid].current_input = 0; - - snapstorage_purge_all(&clients[cid].snapshots); - clients[cid].last_acked_snapshot = -1; - return 0; -} - -static int del_client_callback(int cid, void *user) -{ - /* notify the mod about the drop */ - if(clients[cid].state == SRVCLIENT_STATE_READY || - clients[cid].state == SRVCLIENT_STATE_INGAME) - { - mods_client_drop(cid); - } - - clients[cid].state = SRVCLIENT_STATE_EMPTY; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - snapstorage_purge_all(&clients[cid].snapshots); - return 0; -} - - -static void server_send_map(int cid) -{ - msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL); - msg_pack_string(config.sv_map, 0); - msg_pack_int(current_map_crc); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_heartbeat() -{ - static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; - unsigned short port = config.sv_port; - NETPACKET packet; - - mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); - - packet.client_id = -1; - packet.address = master_server; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2; - packet.data = &data; - - /* supply the set port that the master can use if it has problems */ - if(config.sv_external_port) - port = config.sv_external_port; - data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; - data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; - - netserver_send(net, &packet); -} - -static void server_process_client_packet(NETPACKET *packet) -{ - int cid = packet->client_id; - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - if(sys) - { - /* system message */ - if(msg == NETMSG_INFO) - { - char version[64]; - const char *password; - strncpy(version, msg_unpack_string(), 64); - if(strcmp(version, mods_net_version()) != 0) - { - /* OH FUCK! wrong version, drop him */ - char reason[256]; - sprintf(reason, "wrong version. server is running %s.", mods_net_version()); - netserver_drop(net, cid, reason); - return; - } - - strncpy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); - strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); - password = msg_unpack_string(); - - if(config.password[0] != 0 && strcmp(config.password, password) != 0) - { - /* wrong password */ - netserver_drop(net, cid, "wrong password"); - return; - } - - server_send_map(cid); - } - else if(msg == NETMSG_READY) - { - if(clients[cid].state == SRVCLIENT_STATE_CONNECTING) - { - dbg_msg("server", "player is ready. cid=%x", cid); - clients[cid].state = SRVCLIENT_STATE_READY; - mods_connected(cid); - } - } - else if(msg == NETMSG_ENTERGAME) - { - if(clients[cid].state != SRVCLIENT_STATE_INGAME) - { - dbg_msg("server", "player as entered the game. cid=%x", cid); - clients[cid].state = SRVCLIENT_STATE_INGAME; - mods_client_enter(cid); - } - } - else if(msg == NETMSG_INPUT) - { - int tick, size, i; - CLIENT_INPUT *input; - int64 tagtime; - - clients[cid].last_acked_snapshot = msg_unpack_int(); - if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0) >= 0) - clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); - - tick = msg_unpack_int(); - size = msg_unpack_int(); - - input = &clients[cid].inputs[clients[cid].current_input]; - input->timeleft = server_tick_start_time(tick)-time_get(); - input->pred_tick = tick; - - if(tick <= server_tick()) - { - /* TODO: how should we handle this */ - dbg_msg("server", "input got in late for=%d cur=%d", tick, server_tick()); - tick = server_tick()+1; - } - - input->game_tick = tick; - - for(i = 0; i < size/4; i++) - input->data[i] = msg_unpack_int(); - - clients[cid].current_input++; - clients[cid].current_input %= 200; - } - else if(msg == NETMSG_CMD) - { - const char *pw = msg_unpack_string(); - const char *cmd = msg_unpack_string(); - if(config.rcon_password[0] != 0 && strcmp(pw, config.rcon_password) == 0) - { - dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); - config_set(cmd); - } - } - else - { - dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); - } - } - else - { - /* game message */ - mods_message(msg, cid); - } -} - -static void server_send_serverinfo(NETADDR4 *addr) -{ - NETPACKET packet; - PACKER p; - char buf[128]; - - /* count the players */ - int c = 0; - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - c++; - } - - packer_reset(&p); - packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); - packer_add_string(&p, mods_net_version(), 32); - packer_add_string(&p, config.sv_name, 64); - packer_add_string(&p, config.sv_map, 32); - - /* gametype */ - sprintf(buf, "%d", browseinfo_gametype); - packer_add_string(&p, buf, 2); - - /* flags */ - i = 0; - if(strlen(config.password)) - i |= 1; - sprintf(buf, "%d", i); - packer_add_string(&p, buf, 2); - - /* progression */ - sprintf(buf, "%d", browseinfo_progression); - packer_add_string(&p, buf, 4); - - sprintf(buf, "%d", c); packer_add_string(&p, buf, 3); /* num players */ - sprintf(buf, "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - { - packer_add_string(&p, clients[i].name, 48); /* player name */ - packer_add_string(&p, "0", 6); /* score */ - } - } - - - packet.client_id = -1; - packet.address = *addr; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = packer_size(&p); - packet.data = packer_data(&p); - netserver_send(net, &packet); -} - - -static void server_send_fwcheckresponse(NETADDR4 *addr) -{ - NETPACKET packet; - packet.client_id = -1; - packet.address = *addr; - packet.flags = PACKETFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE); - packet.data = SERVERBROWSE_FWRESPONSE; - netserver_send(net, &packet); -} - -static void server_pump_network() -{ - NETPACKET packet; - - netserver_update(net); - - /* process packets */ - while(netserver_recv(net, &packet)) - { - if(packet.client_id == -1) - { - /* stateless */ - if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) && - memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) - { - server_send_serverinfo(&packet.address); - } - else if(packet.data_size == sizeof(SERVERBROWSE_FWCHECK) && - memcmp(packet.data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) - { - server_send_fwcheckresponse(&packet.address); - } - else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) && - memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) - { - 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) - { - dbg_msg("server", "ERROR: the master server reports that clients can not connect to this server."); - dbg_msg("server", "ERROR: configure your firewall/nat to let trough udp on port %d.", config.sv_port); - } - } - else - server_process_client_packet(&packet); - } -} - -static int server_load_map(const char *mapname) -{ - DATAFILE *df; - char buf[512]; - sprintf(buf, "data/maps/%s.map", mapname); - df = datafile_load(buf); - if(!df) - return 0; - - /* reinit snapshot ids */ - snap_timeout_ids(); - - /* get the crc of the map */ - current_map_crc = datafile_crc(buf); - dbg_msg("server", "%s crc is %08x", buf, current_map_crc); - - strcpy(current_map, mapname); - map_set(df); - return 1; -} - - -static int server_run() -{ - NETADDR4 bindaddr; - - net_init(); - - snap_init_id(); - - /* load map */ - if(!server_load_map(config.sv_map)) - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - return -1; - } - - /* start server */ - if(strlen(config.sv_bindaddr) && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) != 0) - { - /* sweet! */ - } - else - { - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = config.sv_port; - } - - net = netserver_open(bindaddr, config.sv_max_clients, 0); - if(!net) - { - dbg_msg("server", "couldn't open socket. port might already be in use"); - return -1; - } - - netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); - - dbg_msg("server", "server name is '%s'", config.sv_name); - dbg_msg("server", "masterserver is '%s'", config.masterserver); - if(net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0) - { - /* TODO: fix me */ - /*master_server = netaddr4(0, 0, 0, 0, 0); */ - } - - mods_init(); - dbg_msg("server", "version %s", mods_net_version()); - - /* start game */ - { - int64 time_per_heartbeat = time_freq() * 30; - int64 reporttime = time_get(); - int reportinterval = 3; - - int64 simulationtime = 0; - int64 snaptime = 0; - int64 networktime = 0; - int64 totaltime = 0; - - lastheartbeat = 0; - game_start_time = time_get(); - - if(config.debug) - dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024); - - while(1) - { - int64 t = time_get(); - /* load new map TODO: don't poll this */ - if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) - { - config.sv_map_reload = 0; - - /* load map */ - if(server_load_map(config.sv_map)) - { - int c; - - /* new map loaded */ - mods_shutdown(); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - - server_send_map(c); - clients[c].state = SRVCLIENT_STATE_CONNECTING; - clients[c].last_acked_snapshot = -1; - snapstorage_purge_all(&clients[c].snapshots); - } - - game_start_time = time_get(); - current_tick = 0; - mods_init(); - } - else - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - config_set_sv_map(&config, current_map); - } - } - - if(t > server_tick_start_time(current_tick+1)) - { - current_tick++; - - /* apply new input */ - { - int c, i; - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - for(i = 0; i < 200; i++) - { - if(clients[c].inputs[i].game_tick == server_tick()) - { - mods_client_input(c, clients[c].inputs[i].data); - break; - } - } - } - } - - /* progress game */ - { - int64 start = time_get(); - mods_tick(); - simulationtime += time_get()-start; - } - - /* snap game */ - if(config.sv_bandwidth_mode == 0 || - (config.sv_bandwidth_mode == 1 && current_tick%2) || - (config.sv_bandwidth_mode == 2 && (current_tick%3) == 0 )) - /* if(current_tick&1) */ - { - int64 start = time_get(); - server_do_snap(); - snaptime += time_get()-start; - } - } - - if(config.sv_sendheartbeats) - { - if (t > lastheartbeat+time_per_heartbeat) - { - server_send_heartbeat(); - lastheartbeat = t+time_per_heartbeat; - } - } - - { - int64 start = time_get(); - server_pump_network(); - networktime += time_get()-start; - } - - if(reporttime < time_get()) - { - if(config.debug) - { - static NETSTATS prev_stats; - NETSTATS stats; - netserver_stats(net, &stats); - dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms tot=%.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); - - dbg_msg("server", "send=%8d recv=%8d", - (stats.send_bytes - prev_stats.send_bytes)/reportinterval, - (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); - - prev_stats = stats; - } - - simulationtime = 0; - snaptime = 0; - networktime = 0; - totaltime = 0; - - reporttime += time_freq()*reportinterval; - } - totaltime += time_get()-t; - thread_sleep(1); - } - } - - mods_shutdown(); - map_unload(); - - return 0; -} - - -int main(int argc, char **argv) -{ - dbg_msg("server", "starting..."); - engine_init("Teewars", argc, argv); - server_run(); - return 0; -} - diff --git a/src/engine/snapshot.c b/src/engine/snapshot.c deleted file mode 100644 index 4db6ac26..00000000 --- a/src/engine/snapshot.c +++ /dev/null @@ -1,448 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include "snapshot.h" -#include "compression.h" - - -int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); } -int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; } -int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; } -int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; } - -int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); } -char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); } - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index) -{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); } - -int snapshot_get_item_datasize(SNAPSHOT *snap, int index) -{ - if(index == snap->num_items-1) - return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); - return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); -} - -int snapshot_get_item_index(SNAPSHOT *snap, int key) -{ - /* TODO: OPT: this should not be a linear search. very bad */ - int i; - for(i = 0; i < snap->num_items; i++) - { - if(snapitem_key(snapshot_get_item(snap, i)) == key) - return i; - } - return -1; -} - - - -typedef struct -{ - int num_deleted_items; - int num_update_items; - int num_temp_items; /* needed? */ - int data[1]; - - /* - char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; } - - int deleted_item(int index) { return offsets[index]; } - item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); } - item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); } - */ -} SNAPSHOT_DELTA; - - -static const int MAX_ITEMS = 512; -static SNAPSHOT_DELTA empty = {0,0,0,{0}}; - -void *snapshot_empty_delta() -{ - return ∅ -} - -int snapshot_crc(SNAPSHOT *snap) -{ - int crc = 0; - int i, b; - SNAPSHOT_ITEM *item; - int size; - - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - - for(b = 0; b < size/4; b++) - crc += snapitem_data(item)[b]; - } - return crc; -} - -void snapshot_debug_dump(SNAPSHOT *snap) -{ - int size, i, b; - SNAPSHOT_ITEM *item; - - dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items); - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item)); - for(b = 0; b < size/4; b++) - dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]); - } -} - -static int diff_item(int *past, int *current, int *out, int size) -{ - int needed = 0; - while(size) - { - *out = *current-*past; - needed |= *out; - out++; - past++; - current++; - size--; - } - - return needed; -} - -int snapshot_data_rate[0xffff] = {0}; -int snapshot_data_updates[0xffff] = {0}; -static int snapshot_current = 0; - -static void undiff_item(int *past, int *diff, int *out, int size) -{ - while(size) - { - *out = *past+*diff; - - if(*diff == 0) - snapshot_data_rate[snapshot_current] += 1; - else - { - unsigned char buf[16]; - unsigned char *end = vint_pack(buf, *diff); - snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; - } - - out++; - past++; - diff++; - size--; - } -} - - -/* TODO: OPT: this should be made much faster */ -int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata) -{ - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata; - int *data = (int *)delta->data; - int i, itemsize, pastindex; - SNAPSHOT_ITEM *fromitem; - SNAPSHOT_ITEM *curitem; - SNAPSHOT_ITEM *pastitem; - int count = 0; - int size_count = 0; - - delta->num_deleted_items = 0; - delta->num_update_items = 0; - delta->num_temp_items = 0; - - /* pack deleted stuff */ - for(i = 0; i < from->num_items; i++) - { - fromitem = snapshot_get_item(from, i); - if(snapshot_get_item_index(to, (snapitem_key(fromitem))) == -1) - { - /* deleted */ - delta->num_deleted_items++; - *data = snapitem_key(fromitem); - data++; - } - } - - /* pack updated stuff */ - for(i = 0; i < to->num_items; i++) - { - /* do delta */ - itemsize = snapshot_get_item_datasize(to, i); - - curitem = snapshot_get_item(to, i); - pastindex = snapshot_get_item_index(from, snapitem_key(curitem)); - if(pastindex != -1) - { - pastitem = snapshot_get_item(from, pastindex); - if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), data+3, itemsize/4)) - { - *data++ = itemsize; - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - /*data++ = curitem->key();*/ - data += itemsize/4; - delta->num_update_items++; - } - } - else - { - *data++ = itemsize; - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - /*data++ = curitem->key();*/ - - mem_copy(data, snapitem_data(curitem), itemsize); - size_count += itemsize; - data += itemsize/4; - delta->num_update_items++; - count++; - } - } - - if(0) - { - dbg_msg("snapshot", "%d %d %d", - delta->num_deleted_items, - delta->num_update_items, - delta->num_temp_items); - } - - /* - // TODO: pack temp stuff - - // finish - //mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int)); - //mem_copy(delta->data_start(), data, data_size); - //delta->data_size = data_size; - * */ - - if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items) - return 0; - - return (int)((char*)data-(char*)dstdata); -} - -int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size) -{ - SNAPBUILD builder; - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata; - int *data = (int *)delta->data; - SNAPSHOT_ITEM *fromitem; - int i, d, keep, itemsize; - int *deleted; - int id, type, key; - int fromindex; - int *newdata; - - snapbuild_init(&builder); - - /* unpack deleted stuff */ - deleted = data; - data += delta->num_deleted_items; - - /* copy all non deleted stuff */ - for(i = 0; i < from->num_items; i++) - { - /* dbg_assert(0, "fail!"); */ - fromitem = snapshot_get_item(from, i); - itemsize = snapshot_get_item_datasize(from, i); - keep = 1; - for(d = 0; d < delta->num_deleted_items; d++) - { - if(deleted[d] == snapitem_key(fromitem)) - { - keep = 0; - break; - } - } - - if(keep) - { - /* keep it */ - mem_copy( - snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize), - snapitem_data(fromitem), itemsize); - } - } - - /* unpack updated stuff */ - for(i = 0; i < delta->num_update_items; i++) - { - itemsize = *data++; - type = *data++; - id = *data++; - snapshot_current = type; - - key = (type<<16)|id; - - /* create the item if needed */ - newdata = snapbuild_get_item_data(&builder, key); - if(!newdata) - newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize); - - fromindex = snapshot_get_item_index(from, key); - if(fromindex != -1) - { - /* we got an update so we need to apply the diff */ - undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4); - snapshot_data_updates[snapshot_current]++; - } - else /* no previous, just copy the data */ - { - mem_copy(newdata, data, itemsize); - snapshot_data_rate[snapshot_current] += itemsize*8; - snapshot_data_updates[snapshot_current]++; - } - - data += itemsize/4; - } - - /* finish up */ - return snapbuild_finish(&builder, to); -} - -/* SNAPSTORAGE */ - -void snapstorage_init(SNAPSTORAGE *ss) -{ - ss->first = 0; -} - -void snapstorage_purge_all(SNAPSTORAGE *ss) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - SNAPSTORAGE_HOLDER *next; - - while(h) - { - next = h->next; - mem_free(h); - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick) -{ - SNAPSTORAGE_HOLDER *next; - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - next = h->next; - if(h->tick >= tick) - return; /* no more to remove */ - mem_free(h); - - ss->first = next; - next->prev = 0x0; - - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data) -{ - /* allocate memory for holder + snapshot_data */ - SNAPSTORAGE_HOLDER *h = (SNAPSTORAGE_HOLDER *)mem_alloc(sizeof(SNAPSTORAGE_HOLDER)+data_size, 1); - - /* set data */ - h->tick = tick; - h->tagtime = tagtime; - h->snap_size = data_size; - h->snap = (SNAPSHOT*)(h+1); - mem_copy(h->snap, data, data_size); - - /* link */ - h->next = 0; - h->prev = ss->last; - if(ss->last) - ss->last->next = h; - else - ss->first = h; - ss->last = h; -} - -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - if(h->tick == tick) - { - if(tagtime) - *tagtime = h->tagtime; - if(data) - *data = h->snap; - return h->snap_size; - } - - h = h->next; - } - - return -1; -} - -/* SNAPBUILD */ - -void snapbuild_init(SNAPBUILD *sb) -{ - sb->data_size = 0; - sb->num_items = 0; -} - -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index) -{ - return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]); -} - -int *snapbuild_get_item_data(SNAPBUILD *sb, int key) -{ - int i; - for(i = 0; i < sb->num_items; i++) - { - if(snapitem_key(snapbuild_get_item(sb, i)) == key) - return (int *)snapitem_data(snapbuild_get_item(sb, i)); - } - return 0; -} - -int snapbuild_finish(SNAPBUILD *sb, void *snapdata) -{ - /* flattern and make the snapshot */ - SNAPSHOT *snap = (SNAPSHOT *)snapdata; - int offset_size = sizeof(int)*sb->num_items; - snap->data_size = sb->data_size; - snap->num_items = sb->num_items; - mem_copy(snapshot_offsets(snap), sb->offsets, offset_size); - mem_copy(snapshot_datastart(snap), sb->data, sb->data_size); - return sizeof(SNAPSHOT) + offset_size + sb->data_size; -} - -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size) -{ - SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size); - mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size); - obj->type_and_id = (type<<16)|id; - sb->offsets[sb->num_items] = sb->data_size; - sb->data_size += sizeof(SNAPSHOT_ITEM) + size; - sb->num_items++; - - dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data"); - dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items"); - - return snapitem_data(obj); -} diff --git a/src/engine/snapshot.h b/src/engine/snapshot.h deleted file mode 100644 index 60131d4f..00000000 --- a/src/engine/snapshot.h +++ /dev/null @@ -1,91 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef ENGINE_SNAPSHOT_H -#define ENGINE_SNAPSHOT_H - -#include "system.h" - -/* SNAPSHOT */ - -enum -{ - MAX_SNAPSHOT_SIZE=64*1024 -}; - -typedef struct -{ - int type_and_id; -} SNAPSHOT_ITEM; - -typedef struct -{ - int data_size; - int num_items; -} SNAPSHOT; - -int *snapitem_data(SNAPSHOT_ITEM *item); -int snapitem_type(SNAPSHOT_ITEM *item); -int snapitem_id(SNAPSHOT_ITEM *item); -int snapitem_key(SNAPSHOT_ITEM *item); - -int *snapshot_offsets(SNAPSHOT *snap); -char *snapshot_datastart(SNAPSHOT *snap); - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index); -int snapshot_get_item_datasize(SNAPSHOT *snap, int index); -int snapshot_get_item_index(SNAPSHOT *snap, int key); - -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); - -/* SNAPSTORAGE */ - -typedef struct SNAPSTORAGE_HOLDER_t -{ - struct SNAPSTORAGE_HOLDER_t *prev; - struct SNAPSTORAGE_HOLDER_t *next; - - int64 tagtime; - int tick; - - int snap_size; - SNAPSHOT *snap; -} SNAPSTORAGE_HOLDER; - -typedef struct SNAPSTORAGE_t -{ - SNAPSTORAGE_HOLDER *first; - SNAPSTORAGE_HOLDER *last; -} SNAPSTORAGE; - -void snapstorage_init(SNAPSTORAGE *ss); -void snapstorage_purge_all(SNAPSTORAGE *ss); -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick); -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data); -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data); - -/* SNAPBUILD */ - -enum -{ - SNAPBUILD_MAX_ITEMS = 512 -}; - -typedef struct SNAPBUILD -{ - char data[MAX_SNAPSHOT_SIZE]; - int data_size; - - int offsets[SNAPBUILD_MAX_ITEMS]; - int num_items; -} SNAPBUILD; - -void snapbuild_init(SNAPBUILD *sb); -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index); -int *snapbuild_get_item_data(SNAPBUILD *sb, int key); -int snapbuild_finish(SNAPBUILD *sb, void *snapdata); -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size); - -#endif /* ENGINE_SNAPSHOT_H */ diff --git a/src/engine/system.c b/src/engine/system.c deleted file mode 100644 index 8e5bd8e5..00000000 --- a/src/engine/system.c +++ /dev/null @@ -1,723 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include -#include -#include -#include -#include -#include - -#include "detect.h" -#include "system.h" - -#if defined(CONF_FAMILY_UNIX) - #include - #include - - /* unix net includes */ - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #include - #include -#elif defined(CONF_FAMILY_WINDOWS) - #define WIN32_LEAN_AND_MEAN - #define _WIN32_WINNT 0x0400 - #include - #include - #include - #include /* for SHGetFolderPathAndSubDir */ - #include - #include - #include - - #define EWOULDBLOCK WSAEWOULDBLOCK -#else - #error NOT IMPLEMENTED -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - -IOHANDLE logfile = 0; - -void dbg_assert_imp(const char *filename, int line, int test, const char *msg) -{ - if(!test) - { - dbg_msg("assert", "%s(%d): %s", filename, line, msg); - dbg_break(); - } -} - -void dbg_break() -{ - *((unsigned*)0) = 0x0; -} - -int dbg_log_to_file(const char *filename) -{ - logfile = io_open(filename, IOFLAG_WRITE); - if(logfile) - return 1; - return 0; -} - -void dbg_msg(const char *sys, const char *fmt, ...) -{ - va_list args; -#if defined(CONF_FAMILY_WINDOWS) - char str[1024]; - va_start(args, fmt); - vsprintf(str, fmt, args); - va_end(args); - OutputDebugString(str); - OutputDebugString("\n"); -#endif - - va_start(args, fmt); - printf("[%08x][%s]: ", (int)time(0), sys); - vprintf(fmt, args); - va_end(args); - printf("\n"); - fflush(stdout); -} - -int memory_alloced = 0; - -struct memheader -{ - const char *filename; - int line; - int size; - struct memheader *prev; - struct memheader *next; -}; - -struct memtail -{ - int guard; -}; - -struct memheader *first = 0; - -int mem_allocated() -{ - return memory_alloced; -} - -void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment) -{ - /* TODO: fix alignment */ - /* TODO: add debugging */ - struct memheader *header = (struct memheader *)malloc(size+sizeof(struct memheader)+sizeof(struct memtail)); - struct memtail *tail = (struct memtail *)(((char*)(header+1))+size); - header->size = size; - header->filename = filename; - header->line = line; - memory_alloced += header->size; - - tail->guard = 0xbaadc0de; - - header->prev = (struct memheader *)0; - header->next = first; - if(first) - first->prev = header; - first = header; - - /*dbg_msg("mem", "++ %p", header+1); */ - return header+1; -} - -void mem_free(void *p) -{ - if(p) - { - struct memheader *header = (struct memheader *)p - 1; - struct memtail *tail = (struct memtail *)(((char*)(header+1))+header->size); - - if(tail->guard != 0xbaadc0de) - dbg_msg("mem", "!! %p", p); - /* dbg_msg("mem", "-- %p", p); */ - memory_alloced -= header->size; - - if(header->prev) - header->prev->next = header->next; - else - first = header->next; - if(header->next) - header->next->prev = header->prev; - - free(header); - } -} - -void mem_debug_dump() -{ - char buf[1024]; - struct memheader *header = first; - IOHANDLE f = io_open("memory.txt", IOFLAG_WRITE); - - while(header) - { - sprintf(buf, "%s(%d): %d\n", header->filename, header->line, header->size); - io_write(f, buf, strlen(buf)); - header = header->next; - } -} - - -void mem_copy(void *dest, const void *source, unsigned size) -{ - memcpy(dest, source, size); -} - -void mem_move(void *dest, const void *source, unsigned size) -{ - memmove(dest, source, size); -} - -void mem_zero(void *block,unsigned size) -{ - memset(block, 0, size); -} - -IOHANDLE io_open(const char *filename, int flags) -{ - if(flags == IOFLAG_READ) - return (IOHANDLE)fopen(filename, "rb"); - if(flags == IOFLAG_WRITE) - return (IOHANDLE)fopen(filename, "wb"); - return 0x0; -} - -unsigned io_read(IOHANDLE io, void *buffer, unsigned size) -{ - return fread(buffer, 1, size, (FILE*)io); -} - -unsigned io_skip(IOHANDLE io, unsigned size) -{ - fseek((FILE*)io, size, SEEK_CUR); - return size; -} - -int io_seek(IOHANDLE io, int offset, int origin) -{ - int real_origin; - - switch(origin) - { - case IOSEEK_START: - real_origin = SEEK_SET; - break; - case IOSEEK_CUR: - real_origin = SEEK_CUR; - break; - case IOSEEK_END: - real_origin = SEEK_END; - } - - return fseek((FILE*)io, offset, origin); -} - -long int io_tell(IOHANDLE io) -{ - return ftell((FILE*)io); -} - -long int io_length(IOHANDLE io) -{ - long int length; - io_seek(io, 0, IOSEEK_END); - length = io_tell(io); - io_seek(io, 0, IOSEEK_START); - return length; -} - -unsigned io_write(IOHANDLE io, const void *buffer, unsigned size) -{ - return fwrite(buffer, 1, size, (FILE*)io); -} - -int io_close(IOHANDLE io) -{ - fclose((FILE*)io); - return 1; -} - -void thread_sleep(int milliseconds) -{ -#if defined(CONF_FAMILY_UNIX) - usleep(milliseconds*1000); -#elif defined(CONF_FAMILY_WINDOWS) - Sleep(milliseconds); -#else - #error not implemented -#endif -} - -#if defined(CONF_FAMILY_UNIX) -typedef pthread_mutex_t LOCKINTERNAL; -#elif defined(CONF_FAMILY_WINDOWS) -typedef CRITICAL_SECTION LOCKINTERNAL; -#else - #error not implemented on this platform -#endif - -LOCK lock_create() -{ - LOCKINTERNAL *lock = (LOCKINTERNAL*)mem_alloc(sizeof(LOCKINTERNAL), 4); - -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_init(lock, 0x0); -#elif defined(CONF_FAMILY_WINDOWS) - InitializeCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif - return (LOCK)lock; -} - -void lock_destroy(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_destroy((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - DeleteCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif - mem_free(lock); -} - -int lock_try(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - return pthread_mutex_trylock((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - return TryEnterCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif -} - -void lock_wait(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_lock((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - EnterCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif -} - -void lock_release(LOCK lock) -{ -#if defined(CONF_FAMILY_UNIX) - pthread_mutex_unlock((LOCKINTERNAL *)lock); -#elif defined(CONF_FAMILY_WINDOWS) - LeaveCriticalSection((LPCRITICAL_SECTION)lock); -#else - #error not implemented on this platform -#endif -} - -/* ----- time ----- */ -int64 time_get() -{ -#if defined(CONF_FAMILY_UNIX) - struct timeval val; - gettimeofday(&val, NULL); - return (int64)val.tv_sec*(int64)1000000+(int64)val.tv_usec; -#elif defined(CONF_FAMILY_WINDOWS) - static int64 last = 0; - int64 t; - QueryPerformanceCounter((PLARGE_INTEGER)&t); - if(tsin_family = AF_INET; - p->sin_port = htons(src->port); - p->sin_addr.s_addr = htonl(src->ip[0]<<24|src->ip[1]<<16|src->ip[2]<<8|src->ip[3]); -} - -static void sockaddr_to_netaddr4(const struct sockaddr *src, NETADDR4 *dst) -{ - unsigned int ip = htonl(((struct sockaddr_in*)src)->sin_addr.s_addr); - dst->port = htons(((struct sockaddr_in*)src)->sin_port); - dst->ip[0] = (unsigned char)((ip>>24)&0xFF); - dst->ip[1] = (unsigned char)((ip>>16)&0xFF); - dst->ip[2] = (unsigned char)((ip>>8)&0xFF); - dst->ip[3] = (unsigned char)(ip&0xFF); -} - -int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b) -{ - if( a->ip[0] != b->ip[0] || - a->ip[1] != b->ip[1] || - a->ip[2] != b->ip[2] || - a->ip[3] != b->ip[3] || - a->port != b->port - ) - return 1; - return 0; -} - - -int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr) -{ - struct hostent* ip = gethostbyname(hostname); - - if(ip && ip->h_length > 0) - { - addr->ip[0] = ip->h_addr_list[0][0]; - addr->ip[1] = ip->h_addr_list[0][1]; - addr->ip[2] = ip->h_addr_list[0][2]; - addr->ip[3] = ip->h_addr_list[0][3]; - addr->port = port; - return 0; - } - - return -1; -} - -NETSOCKET net_udp4_create(NETADDR4 bindaddr) -{ - struct sockaddr addr; - unsigned int mode = 1; - int broadcast = 1; - - /* create socket */ - int sock = socket(AF_INET, SOCK_DGRAM, 0); - if(sock < 0) - return NETSOCKET_INVALID; - - /* bind, we should check for error */ - netaddr4_to_sockaddr(&bindaddr, &addr); - if(bind(sock, &addr, sizeof(addr)) != 0) - { - net_udp4_close(sock); - return NETSOCKET_INVALID; - } - - /* set non-blocking */ -#if defined(CONF_FAMILY_WINDOWS) - ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); -#else - ioctl(sock, FIONBIO, (unsigned long *)&mode); -#endif - - /* set boardcast */ - setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (const char*)&broadcast, sizeof(broadcast)); - - /* return */ - return sock; -} - -int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size) -{ - struct sockaddr sa; - int d; - mem_zero(&sa, sizeof(sa)); - netaddr4_to_sockaddr(addr, &sa); - d = sendto((int)sock, (const char*)data, size, 0, &sa, sizeof(sa)); - if(d < 0) - dbg_msg("net", "sendto error %d %x", d, d); - return d; -} - -int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize) -{ - struct sockaddr from; - int bytes; - socklen_t fromlen = sizeof(struct sockaddr); - bytes = recvfrom(sock, (char*)data, maxsize, 0, &from, &fromlen); - if(bytes > 0) - { - sockaddr_to_netaddr4(&from, addr); - return bytes; - } - else if(bytes == 0) - return 0; - return -1; /* error */ -} - -int net_udp4_close(NETSOCKET sock) -{ -#if defined(CONF_FAMILY_WINDOWS) - closesocket(sock); -#else - close((int)sock); -#endif - return 0; -} - -NETSOCKET net_tcp4_create(const NETADDR4 *a) -{ - struct sockaddr addr; - - /* create socket */ - int sock = socket(AF_INET, SOCK_STREAM, 0); - if(sock < 0) - return NETSOCKET_INVALID; - - /* bind, we should check for error */ - netaddr4_to_sockaddr(a, &addr); - bind(sock, &addr, sizeof(addr)); - - /* return */ - return sock; -} - -int net_tcp4_set_non_blocking(NETSOCKET sock) -{ - unsigned int mode = 1; -#if defined(CONF_FAMILY_WINDOWS) - return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); -#else - return ioctl(sock, FIONBIO, (unsigned long *)&mode); -#endif -} - -int net_tcp4_set_blocking(NETSOCKET sock) -{ - unsigned int mode = 0; -#if defined(CONF_FAMILY_WINDOWS) - return ioctlsocket(sock, FIONBIO, (unsigned long *)&mode); -#else - return ioctl(sock, FIONBIO, (unsigned long *)&mode); -#endif -} - -int net_tcp4_listen(NETSOCKET sock, int backlog) -{ - return listen(sock, backlog); -} - -int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a) -{ - int s; - socklen_t sockaddr_len; - struct sockaddr addr; - - sockaddr_len = sizeof(addr); - - s = accept(sock, &addr, &sockaddr_len); - - if (s != -1) - { - sockaddr_to_netaddr4(&addr, a); - *new_sock = s; - } - return s; -} - -int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a) -{ - struct sockaddr addr; - - netaddr4_to_sockaddr(a, &addr); - return connect(sock, &addr, sizeof(addr)); -} - -int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a) -{ - struct sockaddr addr; - int res; - - netaddr4_to_sockaddr(a, &addr); - net_tcp4_set_non_blocking(sock); - res = connect(sock, &addr, sizeof(addr)); - net_tcp4_set_blocking(sock); - - return res; -} - -int net_tcp4_send(NETSOCKET sock, const void *data, int size) -{ - int d; - d = send((int)sock, (const char*)data, size, 0); - return d; -} - -int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize) -{ - int bytes; - bytes = recv((int)sock, (char*)data, maxsize, 0); - return bytes; -} - -int net_tcp4_close(NETSOCKET sock) -{ -#if defined(CONF_FAMILY_WINDOWS) - closesocket(sock); -#else - close((int)sock); -#endif - return 0; -} - -int net_errno() -{ - return errno; -} - -int net_would_block() -{ - return net_errno() == EWOULDBLOCK; -} - -int net_init() -{ -#if defined(CONF_FAMILY_WINDOWS) - WSADATA wsaData; - int err = WSAStartup(MAKEWORD(1, 1), &wsaData); - dbg_assert(err == 0, "network initialization failed."); - return err==0?0:1; -#endif - - return 0; -} - -int fs_listdir(const char *dir, fs_listdir_callback cb, void *user) -{ -#if defined(CONF_FAMILY_WINDOWS) - WIN32_FIND_DATA finddata; - HANDLE handle; - char buffer[1024*2]; - strcpy(buffer, dir); - strcat(buffer, "/*"); - - handle = FindFirstFileA(buffer, &finddata); - - if (handle == INVALID_HANDLE_VALUE) - return 0; - - /* add all the entries */ - do - { - if(finddata.cFileName[0] != '.') - cb(finddata.cFileName, 0, user); - } while (FindNextFileA(handle, &finddata)); - - FindClose(handle); - return 0; -#else - struct dirent *entry; - DIR *d = opendir(dir); - - if(!d) - return 0; - - while((entry = readdir(d)) != NULL) - cb(entry->d_name, 0, user); - - /* close the directory and return */ - closedir(d); - return 0; -#endif -} - -int fs_storage_path(const char *appname, char *path, int max) -{ -#if defined(CONF_FAMILY_WINDOWS) - HRESULT r; - char home[MAX_PATH]; - r = SHGetFolderPath (NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, home); - if(r != 0) - return 1; - _snprintf(path, max, "%s/%s", home, appname); - return 0; -#else - char *home = getenv("HOME"); - int i; - if(!home) - return 0; - - snprintf(path, max, "%s/.%s", home, appname); - for(i = strlen(home)+2; path[i]; i++) - path[i] = tolower(path[i]); - - return 1; -#endif -} - -int fs_makedir(const char *path) -{ -#if defined(CONF_FAMILY_WINDOWS) - if(_mkdir(path) == 0) - return 0; - if(errno == EEXIST) - return 0; - return 1; -#else - if(mkdir(path, 0755) == 0) - return 0; - if(errno == EEXIST) - return 0; - return 1; -#endif -} - -void swap_endian(void *data, unsigned elem_size, unsigned num) -{ - char *src = (char*) data; - char *dst = src + (elem_size - 1); - - while(num) - { - unsigned n = elem_size>>1; - char tmp; - while(n) - { - tmp = *src; - *src = *dst; - *dst = tmp; - - src++; - dst--; - n--; - } - - src = src + (elem_size>>1); - dst = src + (elem_size - 1); - num--; - } -} - -#if defined(__cplusplus) -} -#endif diff --git a/src/engine/system.h b/src/engine/system.h deleted file mode 100644 index e54af0f1..00000000 --- a/src/engine/system.h +++ /dev/null @@ -1,531 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#ifndef BASE_SYSTEM_H -#define BASE_SYSTEM_H - -#include "detect.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* Group: Debug */ - -/********** - Function: dbg_assert - - Breaks into the debugger based on a test. - - Parameters: - test - Result of the test. - msg - Message that should be printed if the test fails. - - Remarks: - Does nothing in release version of the library. - - See Also: - -**********/ -void dbg_assert(int test, const char *msg); -#define dbg_assert(test,msg) dbg_assert_imp(__FILE__, __LINE__, test, msg) -void dbg_assert_imp(const char *filename, int line, int test, const char *msg); - -/********** - Function: dbg_break - - Breaks into the debugger. - - Remarks: - Does nothing in release version of the library. - - See Also: - -**********/ -void dbg_break(); - -/********** - Function: dbg_msg - - Prints a debug message. - - Parameters: - sys - A string that describes what system the message belongs to - fmt - A printf styled format string. - - Remarks: - Does nothing in relase version of the library. - - See Also: - -**********/ -void dbg_msg(const char *sys, const char *fmt, ...); - -/* Group: Memory */ - -/********** - Function: mem_alloc - - Allocates memory. - - Parameters: - size - Size of the needed block. - alignment - Alignment for the block. - - Returns: - Returns a pointer to the newly allocated block. Returns a - null pointer if the memory couldn't be allocated. - - Remarks: - - Passing 0 to size will allocated the smallest amount possible - and return a unique pointer. - - See Also: - -**********/ -void *mem_alloc_debug(const char *filename, int line, unsigned size, unsigned alignment); -#define mem_alloc(s,a) mem_alloc_debug(__FILE__, __LINE__, (s), (a)) - -/********** - Function: mem_free - - Frees a block allocated through . - - Remarks: - - In the debug version of the library the function will assert if - a non-valid block is passed, like a null pointer or a block that - isn't allocated. - - See Also: - -**********/ -void mem_free(void *block); - -/********** - Function: mem_copy - Copies a a memory block. - - Parameters: - dest - Destination. - source - Source to copy. - size - Size of the block to copy. - - Remarks: - - This functions DOES NOT handles cases where source and - destination is overlapping. - - See Also: - -**********/ -void mem_copy(void *dest, const void *source, unsigned size); - -/********** - Function: mem_move - Copies a a memory block. - - Parameters: - dest - Destination. - source - Source to copy. - size - Size of the block to copy. - - Remarks: - - This functions handles cases where source and destination is overlapping. - - See Also: - -**********/ -void mem_move(void *dest, const void *source, unsigned size); - -/********** - Function: mem_zero - Sets a complete memory block to 0. - - Parameters: - block - Pointer to the block to zero out. - size - Size of the block. -**********/ -void mem_zero(void *block, unsigned size); - -/* ------- io ------- */ -enum { - IOFLAG_READ = 1, - IOFLAG_WRITE = 2, - IOFLAG_RANDOM = 4, - - IOSEEK_START = 0, - IOSEEK_CUR = 1, - IOSEEK_END = 2 -}; - -typedef struct IOINTERNAL *IOHANDLE; - -/**** Group: File IO ****/ - -/**** - Function: io_open - - Opens a file. - - Parameters: - filename - File to open. - flags - A set of flags. IOFLAG_READ, IOFLAG_WRITE, IOFLAG_RANDOM. - - Returns: - Returns a handle to the file on success and 0 on failure. - -****/ -IOHANDLE io_open(const char *filename, int flags); - -/**** - Function: io_read - - Reads data into a buffer from a file. - - Parameters: - io - Handle to the file to read data from. - buffer - Pointer to the buffer that will recive the data. - size - Number of bytes to read from the file. - - Returns: - Number of bytes read. - -****/ -unsigned io_read(IOHANDLE io, void *buffer, unsigned size); - -/***** - Function: io_skip - - Skips data in a file. - - Parameters: - io - Handle to the file. - size - Number of bytes to skip. - - Returns: - Number of bytes skipped. -****/ -unsigned io_skip(IOHANDLE io, unsigned size); - -/***** - Function: io_write - - Writes data from a buffer to file. - - Parameters: - io - Handle to the file. - buffer - Pointer to the data that should be written. - size - Number of bytes to write. - - Returns: - Number of bytes written. -*****/ -unsigned io_write(IOHANDLE io, const void *buffer, unsigned size); - -/***** - Function: io_seek - - Seeks to a specified offset in the file. - - Parameters: - io - Handle to the file. - offset - Offset from pos to stop. - origin - Position to start searching from. - - Returns: - Returns 0 on success. -*****/ -int io_seek(IOHANDLE io, int offset, int origin); - -/***** - Function: io_tell - - Gets the current position in the file. - - Parameters: - io - Handle to the file. - - Returns: - Returns the current position. -1L if an error occured. -*****/ -long int io_tell(IOHANDLE io); - -/***** - Function: io_length - - Gets the total length of the file. Resetting cursor to the beginning - - Parameters: - io - Handle to the file. - - Returns: - Returns the total size. -1L if an error occured. -*****/ -long int io_length(IOHANDLE io); - -/***** - Function: io_close - - Closes a file. - - Parameters: - io - Handle to the file. - - Returns: - Returns 0 on success. -*****/ -int io_close(IOHANDLE io); - -/**** Group: Threads ****/ - -int thread_create(); /* NOT IMPLEMENTED */ -int thread_destory(); /* NOT IMPLEMENTED */ - -int thread_run(); /* NOT IMPLEMENTED */ -int thread_pause(); /* NOT IMPLEMENTED */ -int thread_wait(); /* NOT IMPLEMENTED */ - -/***** - Function: thread_sleep - - Suspends the current thread for a given period. - - Parameters: - milliseconds - Number of milliseconds to sleep. -*****/ -void thread_sleep(int milliseconds); - -/**** Group: Locks ****/ -typedef void* LOCK; - -LOCK lock_create(); -void lock_destroy(LOCK lock); - -int lock_try(LOCK lock); -void lock_wait(LOCK lock); -void lock_release(LOCK lock); - -/**** Group: Timer ****/ -#ifdef __GNUC__ -/* if compiled with -pedantic-errors it will complain about long - not being a C90 thing. -*/ -__extension__ typedef long long int64; -#else -typedef long long int64; -#endif -/***** - Function: time_get - - Fetches a sample from a high resolution timer. - - Returns: - Current value of the timer. - - Remarks: - To know how fast the timer is ticking, see . -*****/ -int64 time_get(); - -/***** - Function: time_freq - - Returns the frequency of the high resolution timer. - - Returns: - Returns the frequency of the high resolution timer. -*****/ -int64 time_freq(); - -/**** Group: Network General ipv4 ****/ -typedef int NETSOCKET; -enum -{ - NETSOCKET_INVALID = -1 -}; - -typedef struct -{ - unsigned char ip[4]; - unsigned short port; -} NETADDR4; - -/***** - Function: net_host_lookup - - Does a hostname lookup by name and fills out the passed NETADDE4 struct with the recieved details. - - Returns: - 0 on success. -*****/ -int net_host_lookup(const char *hostname, unsigned short port, NETADDR4 *addr); - -/**** Group: Network UDP4 ****/ - -/***** - Function: net_udp4_create - - Creates a UDP4 socket and binds it to a port. - - Parameters: - port - Port to bind the socket to. - - Returns: - On success it returns an handle to the socket. On failure it returns NETSOCKET_INVALID. -*****/ -NETSOCKET net_udp4_create(NETADDR4 bindaddr); - -/***** - Function: net_udp4_send - - Sends a packet over an UDP4 socket. - - Parameters: - sock - Socket to use. - addr - Where to send the packet. - data - Pointer to the packet data to send. - size - Size of the packet. - - Returns: - On success it returns the number of bytes sent. Returns -1 on error. -*****/ -int net_udp4_send(NETSOCKET sock, const NETADDR4 *addr, const void *data, int size); - -/***** - Function: net_udp4_recv - - Recives a packet over an UDP4 socket. - - Parameters: - sock - Socket to use. - addr - Pointer to an NETADDR4 that will recive the address. - data - Pointer to a buffer that will recive the data. - maxsize - Maximum size to recive. - - Returns: - On success it returns the number of bytes recived. Returns -1 on error. -*****/ -int net_udp4_recv(NETSOCKET sock, NETADDR4 *addr, void *data, int maxsize); - -/***** - Function: net_udp4_close - - Closes an UDP4 socket. - - Parameters: - sock - Socket to close. - - Returns: - Returns 0 on success. -1 on error. -*****/ -int net_udp4_close(NETSOCKET sock); - - -/**** Group: Network TCP4 ****/ - -/***** - Function: net_tcp4_create - - DOCTODO: serp -*****/ -NETSOCKET net_tcp4_create(const NETADDR4 *a); - -/***** - Function: net_tcp4_set_non_blocking - - DOCTODO: serp -*****/ -int net_tcp4_set_non_blocking(NETSOCKET sock); - -/***** - Function: net_tcp4_set_non_blocking - - DOCTODO: serp -*****/ -int net_tcp4_set_blocking(NETSOCKET sock); - -/***** - Function: net_tcp4_listen - - DOCTODO: serp -*****/ -int net_tcp4_listen(NETSOCKET sock, int backlog); - -/***** - Function: net_tcp4_accept - - DOCTODO: serp -*****/ -int net_tcp4_accept(NETSOCKET sock, NETSOCKET *new_sock, NETADDR4 *a); - -/***** - Function: net_tcp4_connect - - DOCTODO: serp -*****/ -int net_tcp4_connect(NETSOCKET sock, const NETADDR4 *a); - -/***** - Function: net_tcp4_connect_non_blocking - - DOCTODO: serp -*****/ -int net_tcp4_connect_non_blocking(NETSOCKET sock, const NETADDR4 *a); - -/***** - Function: net_tcp4_send - - DOCTODO: serp -*****/ -int net_tcp4_send(NETSOCKET sock, const void *data, int size); - -/***** - Function: net_tcp4_recv - - DOCTODO: serp -*****/ -int net_tcp4_recv(NETSOCKET sock, void *data, int maxsize); - -/***** - Function: net_tcp4_close - - DOCTODO: serp -*****/ -int net_tcp4_close(NETSOCKET sock); - -/***** - Function: net_errno - - DOCTODO: serp -*****/ -int net_errno(); - -/***** - Function: net_would_block - - DOCTODO: serp -*****/ -int net_would_block(); - -/***** - Function: net_init - - DOCTODO: serp -*****/ -int net_init(); - - - -/* NOT DOCUMENTED */ -typedef void (*fs_listdir_callback)(const char *name, int is_dir, void *user); -int fs_listdir(const char *dir, fs_listdir_callback cb, void *user); -int fs_storage_path(const char *appname, char *path, int max); -int fs_makedir(const char *path); -int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b); - -void mem_debug_dump(); -int mem_allocated(); - -void swap_endian(void *data, unsigned elem_size, unsigned num); -#ifdef __cplusplus -} -#endif - -#endif -- cgit 1.4.1