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 -- 14 files changed, 3532 insertions(+), 3532 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 (limited to 'src/engine/client') 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 -- cgit 1.4.1