diff options
Diffstat (limited to 'src/engine')
| -rw-r--r-- | src/engine/client/ec_client.cpp (renamed from src/engine/client/ec_client.c) | 396 | ||||
| -rw-r--r-- | src/engine/client/ec_gfx.c | 1020 | ||||
| -rw-r--r-- | src/engine/client/ec_gfx.cpp | 992 | ||||
| -rw-r--r-- | src/engine/client/ec_gfx_text.cpp (renamed from src/engine/client/ec_gfx_text.c) | 29 | ||||
| -rw-r--r-- | src/engine/client/ec_inp.cpp (renamed from src/engine/client/ec_inp.c) | 12 | ||||
| -rw-r--r-- | src/engine/client/ec_snd.cpp (renamed from src/engine/client/ec_snd.c) | 22 | ||||
| -rw-r--r-- | src/engine/client/ec_srvbrowse.cpp (renamed from src/engine/client/ec_srvbrowse.c) | 69 | ||||
| -rw-r--r-- | src/engine/e_client_interface.h | 8 | ||||
| -rw-r--r-- | src/engine/e_common_interface.h | 8 | ||||
| -rw-r--r-- | src/engine/e_compression.cpp (renamed from src/engine/e_compression.c) | 62 | ||||
| -rw-r--r-- | src/engine/e_compression.h | 4 | ||||
| -rw-r--r-- | src/engine/e_config.cpp (renamed from src/engine/e_config.c) | 0 | ||||
| -rw-r--r-- | src/engine/e_config.h | 8 | ||||
| -rw-r--r-- | src/engine/e_console.cpp (renamed from src/engine/e_console.c) | 14 | ||||
| -rw-r--r-- | src/engine/e_console.h | 14 | ||||
| -rw-r--r-- | src/engine/e_datafile.cpp (renamed from src/engine/e_datafile.c) | 0 | ||||
| -rw-r--r-- | src/engine/e_demorec.cpp (renamed from src/engine/e_demorec.c) | 33 | ||||
| -rw-r--r-- | src/engine/e_demorec.h | 8 | ||||
| -rw-r--r-- | src/engine/e_engine.cpp (renamed from src/engine/e_engine.c) | 6 | ||||
| -rw-r--r-- | src/engine/e_huffman.cpp (renamed from src/engine/e_huffman.c) | 0 | ||||
| -rw-r--r-- | src/engine/e_huffman.h | 8 | ||||
| -rw-r--r-- | src/engine/e_if_other.h | 3 | ||||
| -rw-r--r-- | src/engine/e_jobs.cpp (renamed from src/engine/e_jobs.c) | 0 | ||||
| -rw-r--r-- | src/engine/e_keynames.cpp (renamed from src/engine/e_keynames.c) | 0 | ||||
| -rw-r--r-- | src/engine/e_linereader.cpp (renamed from src/engine/e_linereader.c) | 0 | ||||
| -rw-r--r-- | src/engine/e_map.cpp (renamed from src/engine/e_map.c) | 0 | ||||
| -rw-r--r-- | src/engine/e_memheap.cpp (renamed from src/engine/e_memheap.c) | 10 | ||||
| -rw-r--r-- | src/engine/e_memheap.h | 2 | ||||
| -rw-r--r-- | src/engine/e_msg.cpp (renamed from src/engine/e_msg.c) | 30 | ||||
| -rw-r--r-- | src/engine/e_network.c | 351 | ||||
| -rw-r--r-- | src/engine/e_network.cpp | 347 | ||||
| -rw-r--r-- | src/engine/e_network.h | 395 | ||||
| -rw-r--r-- | src/engine/e_network_client.c | 154 | ||||
| -rw-r--r-- | src/engine/e_network_client.cpp | 139 | ||||
| -rw-r--r-- | src/engine/e_network_conn.c | 366 | ||||
| -rw-r--r-- | src/engine/e_network_conn.cpp | 349 | ||||
| -rw-r--r-- | src/engine/e_network_internal.h | 156 | ||||
| -rw-r--r-- | src/engine/e_network_server.c | 484 | ||||
| -rw-r--r-- | src/engine/e_network_server.cpp | 408 | ||||
| -rw-r--r-- | src/engine/e_packer.c | 213 | ||||
| -rw-r--r-- | src/engine/e_packer.cpp | 155 | ||||
| -rw-r--r-- | src/engine/e_packer.h | 58 | ||||
| -rw-r--r-- | src/engine/e_ringbuffer.c | 362 | ||||
| -rw-r--r-- | src/engine/e_ringbuffer.cpp | 194 | ||||
| -rw-r--r-- | src/engine/e_ringbuffer.h | 67 | ||||
| -rw-r--r-- | src/engine/e_server_interface.h | 8 | ||||
| -rw-r--r-- | src/engine/e_snapshot.c | 604 | ||||
| -rw-r--r-- | src/engine/e_snapshot.cpp | 571 | ||||
| -rw-r--r-- | src/engine/e_snapshot.h | 160 | ||||
| -rw-r--r-- | src/engine/server/es_register.cpp (renamed from src/engine/server/es_register.c) | 83 | ||||
| -rw-r--r-- | src/engine/server/es_server.c | 1380 | ||||
| -rw-r--r-- | src/engine/server/es_server.cpp | 1969 |
52 files changed, 5976 insertions, 5755 deletions
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.cpp index 0c707ccf..7ba0a2bb 100644 --- a/src/engine/client/ec_client.c +++ b/src/engine/client/ec_client.cpp @@ -28,6 +28,28 @@ #include <mastersrv/mastersrv.h> #include <versionsrv/versionsrv.h> +#include "editor.h" +#include "graphics.h" +#include "client.h" + +static IEditor *m_pEditor = 0; +static IEngineGraphics *m_pGraphics = 0; +IEngineGraphics *Graphics() { return m_pGraphics; } + +static IGameClient *m_pGameClient = 0; + + +class CClient : public IEngine +{ +public: + virtual class IGraphics *Graphics() + { + return m_pGraphics; + } +}; + +static CClient m_Client; + const int prediction_margin = 1000/50/2; /* magic network prediction value */ /* @@ -43,7 +65,7 @@ const int prediction_margin = 1000/50/2; /* magic network prediction value */ */ /* network client, must be accessible from other parts like the server browser */ -NETCLIENT *net; +CNetClient m_NetClient; /* TODO: ugly, fix me */ extern void client_serverbrowse_set(NETADDR *addr, int request, int token, SERVER_INFO *info); @@ -171,22 +193,22 @@ static void graph_render(GRAPH *g, float x, float y, float w, float h, const cha char buf[32]; int i; - gfx_blend_normal(); + //m_pGraphics->BlendNormal(); - gfx_texture_set(-1); + Graphics()->TextureSet(-1); - gfx_quads_begin(); - gfx_setcolor(0, 0, 0, 0.75f); - gfx_quads_drawTL(x, y, w, h); - gfx_quads_end(); + m_pGraphics->QuadsBegin(); + Graphics()->SetColor(0, 0, 0, 0.75f); + Graphics()->QuadsDrawTL(x, y, w, h); + m_pGraphics->QuadsEnd(); - gfx_lines_begin(); - gfx_setcolor(0.95f, 0.95f, 0.95f, 1.00f); - gfx_lines_draw(x, y+h/2, x+w, y+h/2); - gfx_setcolor(0.5f, 0.5f, 0.5f, 0.75f); - 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); + Graphics()->LinesBegin(); + Graphics()->SetColor(0.95f, 0.95f, 0.95f, 1.00f); + Graphics()->LinesDraw(x, y+h/2, x+w, y+h/2); + Graphics()->SetColor(0.5f, 0.5f, 0.5f, 0.75f); + Graphics()->LinesDraw(x, y+(h*3)/4, x+w, y+(h*3)/4); + Graphics()->LinesDraw(x, y+h/4, x+w, y+h/4); for(i = 1; i < GRAPH_MAX; i++) { float a0 = (i-1)/(float)GRAPH_MAX; @@ -197,22 +219,22 @@ static void graph_render(GRAPH *g, float x, float y, float w, float h, const cha float v0 = (g->values[i0]-g->min) / (g->max-g->min); float v1 = (g->values[i1]-g->min) / (g->max-g->min); - gfx_setcolorvertex(0, g->colors[i0][0], g->colors[i0][1], g->colors[i0][2], 0.75f); - gfx_setcolorvertex(1, g->colors[i1][0], g->colors[i1][1], g->colors[i1][2], 0.75f); - gfx_lines_draw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); + Graphics()->SetColorVertex(0, g->colors[i0][0], g->colors[i0][1], g->colors[i0][2], 0.75f); + Graphics()->SetColorVertex(1, g->colors[i1][0], g->colors[i1][1], g->colors[i1][2], 0.75f); + Graphics()->LinesDraw(x+a0*w, y+h-v0*h, x+a1*w, y+h-v1*h); } - gfx_lines_end(); + Graphics()->LinesEnd(); - gfx_texture_set(debug_font); - gfx_quads_text(x+2, y+h-16, 16, 1,1,1,1, description); + Graphics()->TextureSet(debug_font); + Graphics()->QuadsText(x+2, y+h-16, 16, 1,1,1,1, description); str_format(buf, sizeof(buf), "%.2f", g->max); - gfx_quads_text(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf); + Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+2, 16, 1,1,1,1, buf); str_format(buf, sizeof(buf), "%.2f", g->min); - gfx_quads_text(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf); + Graphics()->QuadsText(x+w-8*strlen(buf)-8, y+h-16, 16, 1,1,1,1, buf); } @@ -336,40 +358,40 @@ enum }; /* the game snapshots are modifiable by the game */ -SNAPSTORAGE snapshot_storage; -static SNAPSTORAGE_HOLDER *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0}; +CSnapshotStorage snapshot_storage; +static CSnapshotStorage::CHolder *snapshots[NUM_SNAPSHOT_TYPES] = {0, 0}; static int recived_snapshots = 0; -static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE]; +static char snapshot_incomming_data[CSnapshot::MAX_SIZE]; -static SNAPSTORAGE_HOLDER demorec_snapshotholders[NUM_SNAPSHOT_TYPES]; -static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][MAX_SNAPSHOT_SIZE]; +static CSnapshotStorage::CHolder demorec_snapshotholders[NUM_SNAPSHOT_TYPES]; +static char *demorec_snapshotdata[NUM_SNAPSHOT_TYPES][2][CSnapshot::MAX_SIZE]; /* --- */ void *snap_get_item(int snapid, int index, SNAP_ITEM *item) { - SNAPSHOT_ITEM *i; + CSnapshotItem *i; dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshot_get_item(snapshots[snapid]->alt_snap, index); - item->datasize = snapshot_get_item_datasize(snapshots[snapid]->alt_snap, index); - item->type = snapitem_type(i); - item->id = snapitem_id(i); - return (void *)snapitem_data(i); + i = snapshots[snapid]->m_pAltSnap->GetItem(index); + item->datasize = snapshots[snapid]->m_pAltSnap->GetItemSize(index); + item->type = i->Type(); + item->id = i->ID(); + return (void *)i->Data(); } void snap_invalidate_item(int snapid, int index) { - SNAPSHOT_ITEM *i; + CSnapshotItem *i; dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); - i = snapshot_get_item(snapshots[snapid]->alt_snap, index); + i = snapshots[snapid]->m_pAltSnap->GetItem(index); if(i) { - if((char *)i < (char *)snapshots[snapid]->alt_snap || (char *)i > (char *)snapshots[snapid]->alt_snap + snapshots[snapid]->snap_size) + if((char *)i < (char *)snapshots[snapid]->m_pAltSnap || (char *)i > (char *)snapshots[snapid]->m_pAltSnap + snapshots[snapid]->m_SnapSize) dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); - if((char *)i >= (char *)snapshots[snapid]->snap && (char *)i < (char *)snapshots[snapid]->snap + snapshots[snapid]->snap_size) + if((char *)i >= (char *)snapshots[snapid]->m_pSnap && (char *)i < (char *)snapshots[snapid]->m_pSnap + snapshots[snapid]->m_SnapSize) dbg_msg("ASDFASDFASdf", "ASDFASDFASDF"); - i->type_and_id = -1; + i->m_TypeAndID = -1; } } @@ -381,11 +403,11 @@ void *snap_find_item(int snapid, int type, int id) if(!snapshots[snapid]) return 0x0; - for(i = 0; i < snapshots[snapid]->snap->num_items; i++) + for(i = 0; i < snapshots[snapid]->m_pSnap->m_NumItems; i++) { - SNAPSHOT_ITEM *itm = snapshot_get_item(snapshots[snapid]->alt_snap, i); - if(snapitem_type(itm) == type && snapitem_id(itm) == id) - return (void *)snapitem_data(itm); + CSnapshotItem *itm = snapshots[snapid]->m_pAltSnap->GetItem(i); + if(itm->Type() == type && itm->ID() == id) + return (void *)itm->Data(); } return 0x0; } @@ -395,7 +417,7 @@ int snap_num_items(int snapid) dbg_assert(snapid >= 0 && snapid < NUM_SNAPSHOT_TYPES, "invalid snapid"); if(!snapshots[snapid]) return 0; - return snapshots[snapid]->snap->num_items; + return snapshots[snapid]->m_pSnap->m_NumItems; } /* ------ time functions ------ */ @@ -412,35 +434,34 @@ float client_localtime() { return (time_get()-local_start_time)/(float)(time_fre /* ----- send functions ----- */ int client_send_msg() { - const MSG_INFO *info = msg_get_info(); - NETCHUNK packet; + const MSG_INFO *pInfo = msg_get_info(); + CNetChunk Packet; - if(!info) + if(!pInfo) return -1; if(client_state() == CLIENTSTATE_OFFLINE) return 0; - - mem_zero(&packet, sizeof(NETCHUNK)); + mem_zero(&Packet, sizeof(CNetChunk)); - packet.client_id = 0; - packet.data = info->data; - packet.data_size = info->size; + Packet.m_ClientID = 0; + Packet.m_pData = pInfo->data; + Packet.m_DataSize = pInfo->size; - if(info->flags&MSGFLAG_VITAL) - packet.flags |= NETSENDFLAG_VITAL; - if(info->flags&MSGFLAG_FLUSH) - packet.flags |= NETSENDFLAG_FLUSH; + if(pInfo->flags&MSGFLAG_VITAL) + Packet.m_Flags |= NETSENDFLAG_VITAL; + if(pInfo->flags&MSGFLAG_FLUSH) + Packet.m_Flags |= NETSENDFLAG_FLUSH; - if(info->flags&MSGFLAG_RECORD) + if(pInfo->flags&MSGFLAG_RECORD) { if(demorec_isrecording()) - demorec_record_message(packet.data, packet.data_size); + demorec_record_message(Packet.m_pData, Packet.m_DataSize); } - if(!(info->flags&MSGFLAG_NOSEND)) - netclient_send(net, &packet); + if(!(pInfo->flags&MSGFLAG_NOSEND)) + m_NetClient.Send(&Packet); return 0; } @@ -494,7 +515,7 @@ void client_rcon(const char *cmd) int client_connection_problems() { - return netclient_gotproblems(net); + return m_NetClient.GotProblems(); } void client_direct_input(int *input, int size) @@ -594,7 +615,7 @@ static void client_on_enter_game() /* reset snapshots */ snapshots[SNAP_CURRENT] = 0; snapshots[SNAP_PREV] = 0; - snapstorage_purge_all(&snapshot_storage); + snapshot_storage.PurgeAll(); recived_snapshots = 0; snapshot_parts = 0; current_predtick = 0; @@ -645,7 +666,7 @@ void client_connect(const char *server_address_str) rcon_authed = 0; server_address.port = port; - netclient_connect(net, &server_address); + m_NetClient.Connect(&server_address); client_set_state(CLIENTSTATE_CONNECTING); graph_init(&inputtime_margin_graph, -150.0f, 150.0f); @@ -659,7 +680,7 @@ void client_disconnect_with_reason(const char *reason) /* */ rcon_authed = 0; - netclient_disconnect(net, reason); + m_NetClient.Disconnect(reason); client_set_state(CLIENTSTATE_OFFLINE); map_unload(); @@ -701,7 +722,7 @@ void client_serverinfo_request() static int client_load_data() { - debug_font = gfx_load_texture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE); + debug_font = Graphics()->LoadTexture("debug_font.png", IMG_AUTO, TEXLOAD_NORESAMPLE); return 1; } @@ -721,9 +742,9 @@ static void client_debug_render() if(!config.debug) return; - gfx_blend_normal(); - gfx_texture_set(debug_font); - gfx_mapscreen(0,0,gfx_screenwidth(),gfx_screenheight()); + //m_pGraphics->BlendNormal(); + Graphics()->TextureSet(debug_font); + Graphics()->MapScreen(0,0,Graphics()->ScreenWidth(),Graphics()->ScreenHeight()); if(time_get()-last_snap > time_freq()) { @@ -739,13 +760,13 @@ static void client_debug_render() total = 42 */ frametime_avg = frametime_avg*0.9f + frametime*0.1f; - str_format(buffer, sizeof(buffer), "ticks: %8d %8d mem %dk %d gfxmem: %dk fps: %3d", + str_format(buffer, sizeof(buffer), "ticks: %8d %8d mem %dk %d gfxmem: N/A fps: %3d", current_tick, current_predtick, mem_stats()->allocated/1024, mem_stats()->total_allocations, - gfx_memory_usage()/1024, + /*gfx_memory_usage()/1024, */ // TODO: Refactor: Reenable this (int)(1.0f/frametime_avg)); - gfx_quads_text(2, 2, 16, 1,1,1,1, buffer); + Graphics()->QuadsText(2, 2, 16, 1,1,1,1, buffer); { @@ -761,7 +782,7 @@ static void client_debug_render() str_format(buffer, sizeof(buffer), "send: %3d %5d+%4d=%5d (%3d kbps) avg: %5d\nrecv: %3d %5d+%4d=%5d (%3d kbps) avg: %5d", send_packets, send_bytes, send_packets*42, send_total, (send_total*8)/1024, send_bytes/send_packets, recv_packets, recv_bytes, recv_packets*42, recv_total, (recv_total*8)/1024, recv_bytes/recv_packets); - gfx_quads_text(2, 14, 16, 1,1,1,1, buffer); + Graphics()->QuadsText(2, 14, 16, 1,1,1,1, buffer); } /* render rates */ @@ -774,7 +795,7 @@ static void client_debug_render() { str_format(buffer, sizeof(buffer), "%4d %20s: %8d %8d %8d", i, modc_getitemname(i), snapshot_data_rate[i]/8, snapshot_data_updates[i], (snapshot_data_rate[i]/snapshot_data_updates[i])/8); - gfx_quads_text(2, 100+y*12, 16, 1,1,1,1, buffer); + Graphics()->QuadsText(2, 100+y*12, 16, 1,1,1,1, buffer); y++; } } @@ -783,16 +804,16 @@ static void client_debug_render() str_format(buffer, sizeof(buffer), "pred: %d ms %3.2f", (int)((st_get(&predicted_time, now)-st_get(&game_time, now))*1000/(float)time_freq()), predicted_time.adjustspeed[1]); - gfx_quads_text(2, 70, 16, 1,1,1,1, buffer); + Graphics()->QuadsText(2, 70, 16, 1,1,1,1, buffer); /* render graphs */ if(config.dbg_graphs) { - //gfx_mapscreen(0,0,400.0f,300.0f); - float w = gfx_screenwidth()/4.0f; - float h = gfx_screenheight()/6.0f; - float sp = gfx_screenwidth()/100.0f; - float x = gfx_screenwidth()-w-sp; + //Graphics()->MapScreen(0,0,400.0f,300.0f); + float w = Graphics()->ScreenWidth()/4.0f; + float h = Graphics()->ScreenHeight()/6.0f; + float sp = Graphics()->ScreenWidth()/100.0f; + float x = Graphics()->ScreenWidth()-w-sp; graph_scale_max(&fps_graph); graph_scale_min(&fps_graph); @@ -809,13 +830,13 @@ void client_quit() const char *client_error_string() { - return netclient_error_string(net); + return m_NetClient.ErrorString(); } static void client_render() { if(config.gfx_clear) - gfx_clear(1,1,0); + Graphics()->Clear(1,1,0); modc_render(); client_debug_render(); @@ -888,15 +909,15 @@ static int player_score_comp(const void *a, const void *b) return -1; } -static void client_process_packet(NETCHUNK *packet) +static void client_process_packet(CNetChunk *pPacket) { - if(packet->client_id == -1) + if(pPacket->m_ClientID == -1) { /* connectionlesss */ - if(packet->data_size == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && - memcmp(packet->data, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) + if(pPacket->m_DataSize == (int)(sizeof(VERSIONSRV_VERSION) + sizeof(VERSION_DATA)) && + memcmp(pPacket->m_pData, VERSIONSRV_VERSION, sizeof(VERSIONSRV_VERSION)) == 0) { - unsigned char *versiondata = (unsigned char*) packet->data + sizeof(VERSIONSRV_VERSION); + unsigned char *versiondata = (unsigned char*)pPacket->m_pData + sizeof(VERSIONSRV_VERSION); int version_match = !memcmp(versiondata, VERSION_DATA, sizeof(VERSION_DATA)); dbg_msg("client/version", "version does %s (%d.%d.%d)", @@ -910,12 +931,12 @@ static void client_process_packet(NETCHUNK *packet) } } - if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && - memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_LIST) && + memcmp(pPacket->m_pData, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) { - int size = packet->data_size-sizeof(SERVERBROWSE_LIST); + int size = pPacket->m_DataSize-sizeof(SERVERBROWSE_LIST); int num = size/sizeof(MASTERSRV_ADDR); - MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)packet->data+sizeof(SERVERBROWSE_LIST)); + MASTERSRV_ADDR *addrs = (MASTERSRV_ADDR *)((char*)pPacket->m_pData+sizeof(SERVERBROWSE_LIST)); int i; for(i = 0; i < num; i++) @@ -937,47 +958,47 @@ static void client_process_packet(NETCHUNK *packet) { int packet_type = 0; - if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) packet_type = 2; - if(packet->data_size >= (int)sizeof(SERVERBROWSE_OLD_INFO) && memcmp(packet->data, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0) + if(pPacket->m_DataSize >= (int)sizeof(SERVERBROWSE_OLD_INFO) && memcmp(pPacket->m_pData, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)) == 0) packet_type = 1; if(packet_type) { /* we got ze info */ - UNPACKER up; + CUnpacker up; SERVER_INFO info = {0}; int i; int token = -1; - unpacker_reset(&up, (unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); + up.Reset((unsigned char*)pPacket->m_pData+sizeof(SERVERBROWSE_INFO), pPacket->m_DataSize-sizeof(SERVERBROWSE_INFO)); if(packet_type >= 2) - token = atol(unpacker_get_string(&up)); - str_copy(info.version, unpacker_get_string(&up), sizeof(info.version)); - str_copy(info.name, unpacker_get_string(&up), sizeof(info.name)); - str_copy(info.map, unpacker_get_string(&up), sizeof(info.map)); - str_copy(info.gametype, unpacker_get_string(&up), sizeof(info.gametype)); - 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)); + token = atol(up.GetString()); + str_copy(info.version, up.GetString(), sizeof(info.version)); + str_copy(info.name, up.GetString(), sizeof(info.name)); + str_copy(info.map, up.GetString(), sizeof(info.map)); + str_copy(info.gametype, up.GetString(), sizeof(info.gametype)); + info.flags = atol(up.GetString()); + info.progression = atol(up.GetString()); + info.num_players = atol(up.GetString()); + info.max_players = atol(up.GetString()); str_format(info.address, sizeof(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); + pPacket->m_Address.ip[0], pPacket->m_Address.ip[1], pPacket->m_Address.ip[2], + pPacket->m_Address.ip[3], pPacket->m_Address.port); for(i = 0; i < info.num_players; i++) { - str_copy(info.players[i].name, unpacker_get_string(&up), sizeof(info.players[i].name)); - info.players[i].score = atol(unpacker_get_string(&up)); + str_copy(info.players[i].name, up.GetString(), sizeof(info.players[i].name)); + info.players[i].score = atol(up.GetString()); } - if(!up.error) + if(!up.Error()) { /* sort players */ qsort(info.players, info.num_players, sizeof(*info.players), player_score_comp); - if(net_addr_comp(&server_address, &packet->address) == 0) + if(net_addr_comp(&server_address, &pPacket->m_Address) == 0) { mem_copy(¤t_server_info, &info, sizeof(current_server_info)); current_server_info.netaddr = server_address; @@ -986,9 +1007,9 @@ static void client_process_packet(NETCHUNK *packet) else { if(packet_type == 2) - client_serverbrowse_set(&packet->address, BROWSESET_TOKEN, token, &info); + client_serverbrowse_set(&pPacket->m_Address, BROWSESET_TOKEN, token, &info); else - client_serverbrowse_set(&packet->address, BROWSESET_OLD_INTERNET, -1, &info); + client_serverbrowse_set(&pPacket->m_Address, BROWSESET_OLD_INTERNET, -1, &info); } } } @@ -997,7 +1018,7 @@ static void client_process_packet(NETCHUNK *packet) else { int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys); if(sys) { @@ -1193,15 +1214,15 @@ static void client_process_packet(NETCHUNK *packet) mem_copy((char*)snapshot_incomming_data + part*MAX_SNAPSHOT_PACKSIZE, data, part_size); snapshot_parts |= 1<<part; - if(snapshot_parts == (1<<num_parts)-1) + if(snapshot_parts == (unsigned)((1<<num_parts)-1)) { - static SNAPSHOT emptysnap; - SNAPSHOT *deltashot = &emptysnap; + static CSnapshot emptysnap; + CSnapshot *deltashot = &emptysnap; int purgetick; void *deltadata; int deltasize; - unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer2[CSnapshot::MAX_SIZE]; + unsigned char tmpbuffer3[CSnapshot::MAX_SIZE]; int snapsize; complete_size = (num_parts-1) * MAX_SNAPSHOT_PACKSIZE + part_size; @@ -1210,13 +1231,13 @@ static void client_process_packet(NETCHUNK *packet) snapshot_parts = 0; /* find snapshot that we should use as delta */ - emptysnap.data_size = 0; - emptysnap.num_items = 0; + emptysnap.m_DataSize = 0; + emptysnap.m_NumItems = 0; /* find delta */ if(delta_tick >= 0) { - int deltashot_size = snapstorage_get(&snapshot_storage, delta_tick, 0, &deltashot, 0); + int deltashot_size = snapshot_storage.Get(delta_tick, 0, &deltashot, 0); if(deltashot_size < 0) { @@ -1233,7 +1254,7 @@ static void client_process_packet(NETCHUNK *packet) } /* decompress snapshot */ - deltadata = snapshot_empty_delta(); + deltadata = CSnapshot::EmptyDelta(); deltasize = sizeof(int)*3; if(complete_size) @@ -1249,19 +1270,19 @@ static void client_process_packet(NETCHUNK *packet) /* unpack delta */ purgetick = delta_tick; - snapsize = snapshot_unpack_delta(deltashot, (SNAPSHOT*)tmpbuffer3, deltadata, deltasize); + snapsize = CSnapshot::UnpackDelta(deltashot, (CSnapshot*)tmpbuffer3, deltadata, deltasize); if(snapsize < 0) { dbg_msg("client", "delta unpack failed!"); return; } - if(msg != NETMSG_SNAPEMPTY && snapshot_crc((SNAPSHOT*)tmpbuffer3) != crc) + if(msg != NETMSG_SNAPEMPTY && ((CSnapshot*)tmpbuffer3)->Crc() != crc) { if(config.debug) { dbg_msg("client", "snapshot crc error #%d - tick=%d wantedcrc=%d gotcrc=%d compressed_size=%d delta_tick=%d", - snapcrcerrors, game_tick, crc, snapshot_crc((SNAPSHOT*)tmpbuffer3), complete_size, delta_tick); + snapcrcerrors, game_tick, crc, ((CSnapshot*)tmpbuffer3)->Crc(), complete_size, delta_tick); } snapcrcerrors++; @@ -1282,14 +1303,14 @@ static void client_process_packet(NETCHUNK *packet) /* 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); + if(snapshots[SNAP_PREV] && snapshots[SNAP_PREV]->m_Tick < purgetick) + purgetick = snapshots[SNAP_PREV]->m_Tick; + if(snapshots[SNAP_CURRENT] && snapshots[SNAP_CURRENT]->m_Tick < purgetick) + purgetick = snapshots[SNAP_PREV]->m_Tick; + snapshot_storage.PurgeUntil(purgetick); /* add new */ - snapstorage_add(&snapshot_storage, game_tick, time_get(), snapsize, (SNAPSHOT*)tmpbuffer3, 1); + snapshot_storage.Add(game_tick, time_get(), snapsize, (CSnapshot*)tmpbuffer3, 1); /* add snapshot to demo */ if(demorec_isrecording()) @@ -1320,8 +1341,8 @@ static void client_process_packet(NETCHUNK *packet) st_init(&predicted_time, game_tick*time_freq()/50); predicted_time.adjustspeed[1] = 1000.0f; st_init(&game_time, (game_tick-1)*time_freq()/50); - snapshots[SNAP_PREV] = snapshot_storage.first; - snapshots[SNAP_CURRENT] = snapshot_storage.last; + snapshots[SNAP_PREV] = snapshot_storage.m_pFirst; + snapshots[SNAP_CURRENT] = snapshot_storage.m_pLast; local_start_time = time_get(); client_set_state(CLIENTSTATE_ONLINE); } @@ -1345,8 +1366,8 @@ static void client_process_packet(NETCHUNK *packet) { /* game message */ if(demorec_isrecording()) - demorec_record_message(packet->data, packet->data_size); - /* demorec_record_write("MESG", packet->data_size, ); */ + demorec_record_message(pPacket->m_pData, pPacket->m_DataSize); + /* demorec_record_write("MESG", pPacket->data_size, ); */ modc_message(msg); } @@ -1358,22 +1379,21 @@ int client_mapdownload_totalsize() { return mapdownload_totalsize; } static void client_pump_network() { - NETCHUNK packet; - netclient_update(net); + m_NetClient.Update(); if(client_state() != CLIENTSTATE_DEMOPLAYBACK) { /* check for errors */ - if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE) + if(client_state() != CLIENTSTATE_OFFLINE && m_NetClient.State() == NETSTATE_OFFLINE) { client_set_state(CLIENTSTATE_OFFLINE); client_disconnect(); - dbg_msg("client", "offline error='%s'", netclient_error_string(net)); + dbg_msg("client", "offline error='%s'", m_NetClient.ErrorString()); } /* */ - if(client_state() == CLIENTSTATE_CONNECTING && netclient_state(net) == NETSTATE_ONLINE) + if(client_state() == CLIENTSTATE_CONNECTING && m_NetClient.State() == NETSTATE_ONLINE) { /* we switched to online */ dbg_msg("client", "connected, sending info"); @@ -1383,15 +1403,16 @@ static void client_pump_network() } /* process packets */ - while(netclient_recv(net, &packet)) - client_process_packet(&packet); + CNetChunk Packet; + while(m_NetClient.Recv(&Packet)) + client_process_packet(&Packet); } -static void client_democallback_snapshot(void *data, int size) +static void client_democallback_snapshot(void *pData, int Size) { /* update ticks, they could have changed */ const DEMOREC_PLAYBACKINFO *info = demorec_playback_info(); - SNAPSTORAGE_HOLDER *temp; + CSnapshotStorage::CHolder *temp; current_tick = info->current_tick; prev_tick = info->previous_tick; @@ -1400,8 +1421,8 @@ static void client_democallback_snapshot(void *data, int size) snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; snapshots[SNAP_CURRENT] = temp; - mem_copy(snapshots[SNAP_CURRENT]->snap, data, size); - mem_copy(snapshots[SNAP_CURRENT]->alt_snap, data, size); + mem_copy(snapshots[SNAP_CURRENT]->m_pSnap, pData, Size); + mem_copy(snapshots[SNAP_CURRENT]->m_pAltSnap, pData, Size); modc_newsnapshot(); /*modc_predict();*/ @@ -1476,20 +1497,20 @@ static void client_update() while(1) { - SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT]; - int64 tickstart = (cur->tick)*time_freq()/50; + CSnapshotStorage::CHolder *cur = snapshots[SNAP_CURRENT]; + int64 tickstart = (cur->m_Tick)*time_freq()/50; if(tickstart < now) { - SNAPSTORAGE_HOLDER *next = snapshots[SNAP_CURRENT]->next; + CSnapshotStorage::CHolder *next = snapshots[SNAP_CURRENT]->m_pNext; if(next) { snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; snapshots[SNAP_CURRENT] = next; /* set ticks */ - current_tick = snapshots[SNAP_CURRENT]->tick; - prev_tick = snapshots[SNAP_PREV]->tick; + current_tick = snapshots[SNAP_CURRENT]->m_Tick; + prev_tick = snapshots[SNAP_PREV]->m_Tick; if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV]) { @@ -1506,8 +1527,8 @@ static void client_update() 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; + int64 curtick_start = (snapshots[SNAP_CURRENT]->m_Tick)*time_freq()/50; + int64 prevtick_start = (snapshots[SNAP_PREV]->m_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; @@ -1520,10 +1541,10 @@ static void client_update() prevtick_start = prev_pred_tick*time_freq()/50; predintratick = (pred_now - prevtick_start) / (float)(curtick_start-prevtick_start); - if(new_pred_tick < snapshots[SNAP_PREV]->tick-SERVER_TICK_SPEED || new_pred_tick > snapshots[SNAP_PREV]->tick+SERVER_TICK_SPEED) + if(new_pred_tick < snapshots[SNAP_PREV]->m_Tick-SERVER_TICK_SPEED || new_pred_tick > snapshots[SNAP_PREV]->m_Tick+SERVER_TICK_SPEED) { dbg_msg("client", "prediction time reset!"); - st_init(&predicted_time, snapshots[SNAP_CURRENT]->tick*time_freq()/50); + st_init(&predicted_time, snapshots[SNAP_CURRENT]->m_Tick*time_freq()/50); } if(new_pred_tick > current_predtick) @@ -1606,27 +1627,24 @@ static void client_versionupdate() { if(jobs_status(&version_serveraddr.job) == JOBSTATUS_DONE) { - NETCHUNK packet; + CNetChunk Packet; - mem_zero(&packet, sizeof(NETCHUNK)); + mem_zero(&Packet, sizeof(Packet)); version_serveraddr.addr.port = VERSIONSRV_PORT; - packet.client_id = -1; - packet.address = version_serveraddr.addr; - packet.data = VERSIONSRV_GETVERSION; - packet.data_size = sizeof(VERSIONSRV_GETVERSION); - packet.flags = NETSENDFLAG_CONNLESS; + Packet.m_ClientID = -1; + Packet.m_Address = version_serveraddr.addr; + Packet.m_pData = VERSIONSRV_GETVERSION; + Packet.m_DataSize = sizeof(VERSIONSRV_GETVERSION); + Packet.m_Flags = NETSENDFLAG_CONNLESS; - netclient_send(net, &packet); + m_NetClient.Send(&Packet); state++; } } } -extern int editor_update_and_render(); -extern void editor_init(); - static void client_run() { NETADDR bindaddr; @@ -1640,14 +1658,16 @@ static void client_run() snapshot_parts = 0; /* init graphics and sound */ - if(gfx_init() != 0) + m_pGraphics = CreateEngineGraphics(); + if(m_pGraphics->Init() != 0) return; /* start refreshing addresses while we load */ mastersrv_refresh_addresses(); /* init the editor */ - editor_init(); + m_pEditor = CreateEditor(); + m_pEditor->Init(m_pGraphics); /* sound is allowed to fail */ snd_init(); @@ -1657,12 +1677,13 @@ static void client_run() return; /* init the mod */ + m_pGameClient = CreateGameClient(&m_Client); modc_init(); dbg_msg("client", "version %s", modc_net_version()); /* open socket */ mem_zero(&bindaddr, sizeof(bindaddr)); - net = netclient_open(bindaddr, 0); + m_NetClient.Open(bindaddr, 0); /* connect to the server if wanted */ /* @@ -1714,7 +1735,7 @@ static void client_run() } /* release focus */ - if(!gfx_window_active()) + if(!Graphics()->WindowActive()) { if(window_must_refocus == 0) inp_mouse_mode_absolute(); @@ -1727,7 +1748,7 @@ static void client_run() } /* refocus */ - if(window_must_refocus && gfx_window_active()) + if(window_must_refocus && Graphics()->WindowActive()) { if(window_must_refocus < 3) { @@ -1767,8 +1788,8 @@ static void client_run() if(config.cl_editor) { client_update(); - editor_update_and_render(); - gfx_swap(); + m_pEditor->UpdateAndRender(); + m_pGraphics->Swap(); } else { @@ -1784,7 +1805,7 @@ static void client_run() if((frames%10) == 0) { client_render(); - gfx_swap(); + m_pGraphics->Swap(); } } else @@ -1799,7 +1820,7 @@ static void client_run() { static PERFORMACE_INFO scope = {"gfx_swap", 0}; perf_start(&scope); - gfx_swap(); + m_pGraphics->Swap(); perf_end(); } } @@ -1815,7 +1836,7 @@ static void client_run() /* be nice */ if(config.dbg_stress) thread_sleep(5); - else if(config.cl_cpu_throttle || !gfx_window_active()) + else if(config.cl_cpu_throttle || !Graphics()->WindowActive()) thread_sleep(1); if(config.dbg_hitch) @@ -1832,7 +1853,7 @@ static void client_run() frames/(float)(reportinterval/time_freq()), 1.0f/frametime_high, 1.0f/frametime_low, - netclient_state(net)); + m_NetClient.State()); } frametime_low = 1; frametime_high = 0; @@ -1857,10 +1878,15 @@ static void client_run() modc_shutdown(); client_disconnect(); - gfx_shutdown(); + m_pGraphics->Shutdown(); snd_shutdown(); } +void gfx_swap() +{ + m_pGraphics->Swap(); +} + static void con_connect(void *result, void *user_data) { str_copy(cmd_connect, console_arg_string(result, 0), sizeof(cmd_connect)); @@ -1886,7 +1912,7 @@ static void con_ping(void *result, void *user_data) static void con_screenshot(void *result, void *user_data) { - gfx_screenshot(); + Graphics()->TakeScreenshot(); } static void con_rcon(void *result, void *user_data) @@ -1911,7 +1937,7 @@ const char *client_demoplayer_play(const char *filename) int crc; const char *error; client_disconnect(); - netclient_error_string_reset(net); + m_NetClient.ResetErrorString(); /* try to start playback */ demorec_playback_registercallbacks(client_democallback_snapshot, client_democallback_message); @@ -1939,15 +1965,15 @@ const char *client_demoplayer_play(const char *filename) snapshots[SNAP_CURRENT] = &demorec_snapshotholders[SNAP_CURRENT]; snapshots[SNAP_PREV] = &demorec_snapshotholders[SNAP_PREV]; - snapshots[SNAP_CURRENT]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][0]; - snapshots[SNAP_CURRENT]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_CURRENT][1]; - snapshots[SNAP_CURRENT]->snap_size = 0; - snapshots[SNAP_CURRENT]->tick = -1; + snapshots[SNAP_CURRENT]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][0]; + snapshots[SNAP_CURRENT]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_CURRENT][1]; + snapshots[SNAP_CURRENT]->m_SnapSize = 0; + snapshots[SNAP_CURRENT]->m_Tick = -1; - snapshots[SNAP_PREV]->snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][0]; - snapshots[SNAP_PREV]->alt_snap = (SNAPSHOT *)demorec_snapshotdata[SNAP_PREV][1]; - snapshots[SNAP_PREV]->snap_size = 0; - snapshots[SNAP_PREV]->tick = -1; + snapshots[SNAP_PREV]->m_pSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][0]; + snapshots[SNAP_PREV]->m_pAltSnap = (CSnapshot *)demorec_snapshotdata[SNAP_PREV][1]; + snapshots[SNAP_PREV]->m_SnapSize = 0; + snapshots[SNAP_PREV]->m_Tick = -1; /* enter demo playback state */ client_set_state(CLIENTSTATE_DEMOPLAYBACK); diff --git a/src/engine/client/ec_gfx.c b/src/engine/client/ec_gfx.c deleted file mode 100644 index 1ea9f407..00000000 --- a/src/engine/client/ec_gfx.c +++ /dev/null @@ -1,1020 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ - -#include <base/detect.h> - -#include "SDL.h" - -#ifdef CONF_FAMILY_WINDOWS - #define WIN32_LEAN_AND_MEAN - #include <windows.h> -#endif - -#ifdef CONF_PLATFORM_MACOSX - #include <OpenGL/gl.h> - #include <OpenGL/glu.h> -#else - #include <GL/gl.h> - #include <GL/glu.h> -#endif - -#include <base/system.h> -#include <engine/external/pnglite/pnglite.h> - -#include <engine/e_client_interface.h> -#include <engine/e_engine.h> -#include <engine/e_config.h> -#include <engine/e_keys.h> - -#include <string.h> -#include <stdio.h> -#include <math.h> - -/* compressed textures */ -#define GL_COMPRESSED_RGB_ARB 0x84ED -#define GL_COMPRESSED_RGBA_ARB 0x84EE -#define GL_COMPRESSED_ALPHA_ARB 0x84E9 - -#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE - -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 int no_gfx = 0; - -static COLOR color[4]; -static TEXCOORD texture[4]; - -static int do_screenshot = 0; -static int render_enable = 1; - -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; - -static int invalid_texture = 0; - -typedef struct -{ - GLuint tex; - int memsize; - int flags; - int next; -} TEXTURE; - -enum -{ - MAX_TEXTURES = 1024*4 -}; - -static TEXTURE textures[MAX_TEXTURES]; -static int first_free_texture; -static int memory_usage = 0; - -static SDL_Surface *screen_surface; - -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, -}; - -static void flush() -{ - if(num_vertices == 0) - return; - - if(no_gfx) - { - num_vertices = 0; - return; - } - - - //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - //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(render_enable) - { - 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 add_vertices(int count) -{ - num_vertices += count; - if((num_vertices + count) >= vertex_buffer_size) - flush(); -} - -static int try_init() -{ - const SDL_VideoInfo *info; - int flags = SDL_OPENGL; - - screen_width = config.gfx_screen_width; - screen_height = config.gfx_screen_height; - - info = SDL_GetVideoInfo(); - - /* set flags */ - flags = SDL_OPENGL; - flags |= SDL_GL_DOUBLEBUFFER; - flags |= SDL_HWPALETTE; - if(config.dbg_resizable) - flags |= SDL_RESIZABLE; - - if(info->hw_available) - flags |= SDL_HWSURFACE; - else - flags |= SDL_SWSURFACE; - - if(info->blit_hw) - flags |= SDL_HWACCEL; - - if(config.gfx_fullscreen) - flags |= SDL_FULLSCREEN; - - /* set gl attributes */ - if(config.gfx_fsaa_samples) - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.gfx_fsaa_samples); - } - else - { - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); - SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); - } - - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, config.gfx_vsync); - - /* set caption */ - SDL_WM_SetCaption("Teeworlds", "Teeworlds"); - - /* create window */ - screen_surface = SDL_SetVideoMode(screen_width, screen_height, 0, flags); - if(screen_surface == NULL) - { - dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); - return -1; - } - - return 0; -} - -void gfx_font_init(); - -static int gfx_init_window() -{ - if(try_init() == 0) - return 0; - - /* try disabling fsaa */ - while(config.gfx_fsaa_samples) - { - config.gfx_fsaa_samples--; - - if(config.gfx_fsaa_samples) - dbg_msg("gfx", "lowering FSAA to %d and trying again", config.gfx_fsaa_samples); - else - dbg_msg("gfx", "disabling FSAA and trying again"); - - if(try_init() == 0) - return 0; - } - - /* try lowering the resolution */ - if(config.gfx_screen_width != 640 || config.gfx_screen_height != 480) - { - dbg_msg("gfx", "setting resolution to 640x480 and trying again"); - config.gfx_screen_width = 640; - config.gfx_screen_height = 480; - - if(try_init() == 0) - return 0; - } - - dbg_msg("gfx", "out of ideas. failed to init graphics"); - - return -1; -} - -int gfx_init() -{ - int i; - - if(config.dbg_stress) - no_gfx = 1; - - { - int systems = 0; - - if(!no_gfx) - systems |= SDL_INIT_VIDEO; - - if(config.snd_enable) - systems |= SDL_INIT_AUDIO; - - if(config.cl_eventthread) - systems |= SDL_INIT_EVENTTHREAD; - - if(SDL_Init(systems) < 0) - { - dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError()); - return -1; - } - } - - atexit(SDL_Quit); - - if(!no_gfx) - { - #ifdef CONF_FAMILY_WINDOWS - if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) - putenv("SDL_VIDEO_WINDOW_POS=8,27"); - #endif - - if(gfx_init_window() != 0) - return -1; - } - - /* 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());*/ - - - /* 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; - - if(!no_gfx) - { - SDL_ShowCursor(0); - 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(); - - glAlphaFunc(GL_GREATER, 0); - glEnable(GL_ALPHA_TEST); - glDepthMask(0); - - - //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 */ - invalid_texture = gfx_load_texture_raw(4,4,IMG_RGBA,null_texture_data,IMG_RGBA,TEXLOAD_NORESAMPLE); - dbg_msg("", "invalid texture id: %d %d", invalid_texture, textures[invalid_texture].tex); - - - /* font init */ - gfx_font_init(); - /* perform some tests */ - /* pixeltest_dotesting(); */ - - /*if(config.dbg_stress) - gfx_minimize();*/ - - return 0; -} - -float gfx_screenaspect() -{ - return gfx_screenwidth()/(float)gfx_screenheight(); -} - -int gfx_window_active() -{ - return SDL_GetAppState()&SDL_APPINPUTFOCUS; -} - -int gfx_window_open() -{ - return SDL_GetAppState()&SDL_APPACTIVE; -} - -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) -{ - int num_modes = sizeof(fakemodes)/sizeof(VIDEO_MODE); - SDL_Rect **modes; - - 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; - } - - /* TODO: fix this code on osx or windows */ - - modes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN); - if(modes == NULL) - { - /* no modes */ - num_modes = 0; - } - else if(modes == (SDL_Rect**)-1) - { - /* all modes */ - } - else - { - int i; - num_modes = 0; - for(i = 0; modes[i]; ++i) - { - if(num_modes == maxcount) - break; - list[num_modes].width = modes[i]->w; - list[num_modes].height = modes[i]->h; - list[num_modes].red = 8; - list[num_modes].green = 8; - list[num_modes].blue = 8; - num_modes++; - } - } - - return num_modes; -} - -int gfx_unload_texture(int index) -{ - if(index == invalid_texture) - return 0; - - if(index < 0) - return 0; - - 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_none() -{ - if(no_gfx) return; - glDisable(GL_BLEND); -} - -void gfx_blend_normal() -{ - if(no_gfx) return; - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); -} - -void gfx_blend_additive() -{ - if(no_gfx) return; - glEnable(GL_BLEND); - 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 store_format, int flags) -{ - int mipmap = 1; - unsigned char *texdata = (unsigned char *)data; - unsigned char *tmpdata = 0; - int oglformat = 0; - int store_oglformat = 0; - int tex = 0; - - /* don't waste memory on texture if we are stress testing */ - if(config.dbg_stress || no_gfx) - return invalid_texture; - - /* grab texture */ - tex = first_free_texture; - first_free_texture = textures[tex].next; - textures[tex].next = -1; - - /* resample if needed */ - if(!(flags&TEXLOAD_NORESAMPLE) && 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; - } - } - - oglformat = GL_RGBA; - if(format == IMG_RGB) - oglformat = GL_RGB; - else if(format == IMG_ALPHA) - oglformat = GL_ALPHA; - - /* upload texture */ - if(config.gfx_texture_compression) - { - store_oglformat = GL_COMPRESSED_RGBA_ARB; - if(store_format == IMG_RGB) - store_oglformat = GL_COMPRESSED_RGB_ARB; - else if(store_format == IMG_ALPHA) - store_oglformat = GL_COMPRESSED_ALPHA_ARB; - } - else - { - store_oglformat = GL_RGBA; - if(store_format == IMG_RGB) - store_oglformat = GL_RGB; - else if(store_format == IMG_ALPHA) - store_oglformat = GL_ALPHA; - } - - 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_NEAREST); - gluBuild2DMipmaps(GL_TEXTURE_2D, store_oglformat, w, h, oglformat, GL_UNSIGNED_BYTE, texdata); - - /* calculate memory usage */ - { - int pixel_size = 4; - if(store_format == IMG_RGB) - pixel_size = 3; - else if(store_format == IMG_ALPHA) - pixel_size = 1; - - textures[tex].memsize = w*h*pixel_size; - if(mipmap) - { - while(w > 2 && h > 2) - { - w>>=1; - h>>=1; - textures[tex].memsize += w*h*pixel_size; - } - } - } - - memory_usage += textures[tex].memsize; - mem_free(tmpdata); - return tex; -} - -/* simple uncompressed RGBA loaders */ -int gfx_load_texture(const char *filename, int store_format, int flags) -{ - int l = strlen(filename); - int id; - IMAGE_INFO img; - - if(l < 3) - return -1; - if(gfx_load_png(&img, filename)) - { - if (store_format == IMG_AUTO) - store_format = img.format; - - id = gfx_load_texture_raw(img.width, img.height, img.format, img.data, store_format, flags); - mem_free(img.data); - return id; - } - - return invalid_texture; -} - -int gfx_load_png(IMAGE_INFO *img, const char *filename) -{ - char completefilename[512]; - unsigned char *buffer; - png_t png; - - /* open file for reading */ - png_init(0,0); - - engine_getpath(completefilename, sizeof(completefilename), filename, IOFLAG_READ); - - if(png_open_file(&png, completefilename) != PNG_NO_ERROR) - { - dbg_msg("game/png", "failed to open file. filename='%s'", completefilename); - 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'", completefilename); - png_close_file(&png); - return 0; - } - - 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); - - /* TODO: SDL, is this correct? */ - SDL_Quit(); -} - -void gfx_screenshot() -{ - do_screenshot = 1; -} - - -extern int text_render_codepaths_usage[5]; - -void gfx_swap() -{ - /*dbg_msg("", "%d %d %d %d %d", - text_render_codepaths_usage[0], - text_render_codepaths_usage[1], - text_render_codepaths_usage[2], - text_render_codepaths_usage[3], - text_render_codepaths_usage[4]); - - text_render_codepaths_usage[0] = 0; - text_render_codepaths_usage[1] = 0; - text_render_codepaths_usage[2] = 0; - text_render_codepaths_usage[3] = 0; - text_render_codepaths_usage[4] = 0;*/ - - if(do_screenshot) - { - /* find filename */ - char filename[128]; - static int index = 1; - - for(; index < 1000; index++) - { - IOHANDLE io; - str_format(filename, sizeof(filename), "screenshots/screenshot%04d.png", index); - io = engine_openfile(filename, IOFLAG_READ); - if(io) - io_close(io); - else - break; - } - - gfx_screenshot_direct(filename); - - do_screenshot = 0; - } - - { - static PERFORMACE_INFO pscope = {"glfwSwapBuffers", 0}; - perf_start(&pscope); - SDL_GL_SwapBuffers(); - perf_end(); - } - - if(render_enable && config.gfx_finish) - glFinish(); -} - -void gfx_screenshot_direct(const char *filename) -{ - /* fetch image data */ - int y; - int w = screen_width; - int h = screen_height; - unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*4, 1); - unsigned char *temp_row = pixel_data+w*h*4; - glReadPixels(0,0, w, h, GL_RGBA, 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*4, w*4); - mem_copy(pixel_data+y*w*4, pixel_data+(h-y-1)*w*4, w*4); - mem_copy(pixel_data+(h-y-1)*w*4, temp_row,w*4); - } - - /* find filename */ - { - char wholepath[1024]; - png_t png; - - engine_savepath(filename, wholepath, sizeof(wholepath)); - - /* 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_ALPHA, (unsigned char *)pixel_data); - png_close_file(&png); - } - - /* clean up */ - mem_free(pixel_data); -} - -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(no_gfx) return; - 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) -{ - if(no_gfx) return; - glClearColor(r,g,b,0.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; - if(no_gfx) return; - 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_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[1].u = br_u; - texture[0].v = tl_v; texture[1].v = tl_v; - - texture[3].u = tl_u; texture[2].u = br_u; - texture[3].v = br_v; texture[2].v = br_v; -} - -void gfx_quads_setsubset_free( - float x0, float y0, float x1, float y1, - float x2, float y2, float x3, float y3) -{ - texture[0].u = x0; texture[0].v = y0; - texture[1].u = x1; texture[1].v = y1; - texture[2].u = x2; texture[2].v = y2; - texture[3].u = x3; texture[3].v = y3; -} - - -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); - - add_vertices(4); -} - -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]; - - add_vertices(4); -} - -void gfx_quads_text(float x, float y, float size, float r, float g, float b, float a, const char *text) -{ - float startx = x; - - gfx_quads_begin(); - gfx_setcolor(r,g,b,a); - - 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(); -} - -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]; - - add_vertices(2); -} - -void gfx_clip_enable(int x, int y, int w, int h) -{ - if(no_gfx) return; - glScissor(x, gfx_screenheight()-(y+h), w, h); - glEnable(GL_SCISSOR_TEST); -} - -void gfx_clip_disable() -{ - if(no_gfx) return; - glDisable(GL_SCISSOR_TEST); -} - -void gfx_minimize() -{ - SDL_WM_IconifyWindow(); -} - -void gfx_maximize() -{ - /* TODO: SDL */ -} diff --git a/src/engine/client/ec_gfx.cpp b/src/engine/client/ec_gfx.cpp new file mode 100644 index 00000000..5632581a --- /dev/null +++ b/src/engine/client/ec_gfx.cpp @@ -0,0 +1,992 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ + +#include <base/detect.h> + +#include "SDL.h" + +#ifdef CONF_FAMILY_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include <windows.h> +#endif + +#ifdef CONF_PLATFORM_MACOSX + #include <OpenGL/gl.h> + #include <OpenGL/glu.h> +#else + #include <GL/gl.h> + #include <GL/glu.h> +#endif + +#include <base/system.h> +#include <engine/external/pnglite/pnglite.h> + +#include <engine/e_client_interface.h> +#include <engine/e_engine.h> +#include <engine/e_config.h> +#include <engine/e_keys.h> + +#include <string.h> +#include <stdio.h> +#include <math.h> + +/* compressed textures */ +#define GL_COMPRESSED_RGB_ARB 0x84ED +#define GL_COMPRESSED_RGBA_ARB 0x84EE +#define GL_COMPRESSED_ALPHA_ARB 0x84E9 + +#define TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + + +void gfx_font_init(); + +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) +{ + int num_modes = sizeof(fakemodes)/sizeof(VIDEO_MODE); + SDL_Rect **modes; + + 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; + } + + /* TODO: fix this code on osx or windows */ + + modes = SDL_ListModes(NULL, SDL_OPENGL|SDL_GL_DOUBLEBUFFER|SDL_FULLSCREEN); + if(modes == NULL) + { + /* no modes */ + num_modes = 0; + } + else if(modes == (SDL_Rect**)-1) + { + /* all modes */ + } + else + { + int i; + num_modes = 0; + for(i = 0; modes[i]; ++i) + { + if(num_modes == maxcount) + break; + list[num_modes].width = modes[i]->w; + list[num_modes].height = modes[i]->h; + list[num_modes].red = 8; + list[num_modes].green = 8; + list[num_modes].blue = 8; + num_modes++; + } + } + + return num_modes; +} + + +#include "graphics.h" + +class CGraphics_OpenGL : public IEngineGraphics +{ +protected: + /* */ + typedef struct { float x, y, z; } CPoint; + typedef struct { float u, v; } CTexCoord; + typedef struct { float r, g, b, a; } CColor; + + typedef struct + { + CPoint m_Pos; + CTexCoord m_Tex; + CColor m_Color; + } CVertex; + + enum + { + MAX_VERTICES = 32*1024, + MAX_TEXTURES = 1024*4, + + DRAWING_QUADS=1, + DRAWING_LINES=2 + }; + + CVertex m_aVertices[MAX_VERTICES]; + int m_NumVertices; + + CColor m_aColor[4]; + CTexCoord m_aTexture[4]; + + bool m_RenderEnable; + + float m_Rotation; + int m_Drawing; + bool m_DoScreenshot; + + float m_ScreenX0; + float m_ScreenY0; + float m_ScreenX1; + float m_ScreenY1; + + int m_InvalidTexture; + + struct CTexture + { + GLuint tex; + int memsize; + int flags; + int next; + }; + + enum + { + + }; + + CTexture m_aTextures[MAX_TEXTURES]; + int m_FirstFreeTexture; + int m_TextureMemoryUsage; + + + void Flush() + { + if(m_NumVertices == 0) + return; + + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glVertexPointer(3, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices); + glTexCoordPointer(2, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices + sizeof(float)*3); + glColorPointer(4, GL_FLOAT, + sizeof(CVertex), + (char*)m_aVertices + sizeof(float)*5); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + + if(m_RenderEnable) + { + if(m_Drawing == DRAWING_QUADS) + glDrawArrays(GL_QUADS, 0, m_NumVertices); + else if(m_Drawing == DRAWING_LINES) + glDrawArrays(GL_LINES, 0, m_NumVertices); + } + + /* Reset pointer */ + m_NumVertices = 0; + } + + void AddVertices(int count) + { + m_NumVertices += count; + if((m_NumVertices + count) >= MAX_VERTICES) + Flush(); + } + + void Rotate(CPoint *pCenter, CPoint *pPoint) + { + float x = pPoint->x - pCenter->x; + float y = pPoint->y - pCenter->y; + pPoint->x = x * cosf(m_Rotation) - y * sinf(m_Rotation) + pCenter->x; + pPoint->y = x * sinf(m_Rotation) + y * cosf(m_Rotation) + pCenter->y; + } + + + + + 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; + } +public: + CGraphics_OpenGL() + { + m_NumVertices = 0; + + m_ScreenX0 = 0; + m_ScreenY0 = 0; + m_ScreenX1 = 0; + m_ScreenY1 = 0; + + m_ScreenWidth = -1; + m_ScreenHeight = -1; + + m_Rotation = 0; + m_Drawing = 0; + m_InvalidTexture = 0; + + m_TextureMemoryUsage = 0; + + m_RenderEnable = true; + m_DoScreenshot = false; + } + + + virtual void ClipEnable(int x, int y, int w, int h) + { + //if(no_gfx) return; + glScissor(x, ScreenHeight()-(y+h), w, h); + glEnable(GL_SCISSOR_TEST); + } + + virtual void ClipDisable() + { + //if(no_gfx) return; + glDisable(GL_SCISSOR_TEST); + } + + + virtual void BlendNone() + { + glDisable(GL_BLEND); + } + + virtual void BlendNormal() + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + virtual void BlendAdditive() + { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + } + + //int gfx_memory_usage() { return m_MemoryUsage; } + + virtual void MapScreen(float tl_x, float tl_y, float br_x, float br_y) + { + m_ScreenX0 = tl_x; + m_ScreenY0 = tl_y; + m_ScreenX1 = br_x; + m_ScreenY1 = br_y; + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(tl_x, br_x, br_y, tl_y, 1.0f, 10.f); + } + + virtual void GetScreen(float *tl_x, float *tl_y, float *br_x, float *br_y) + { + *tl_x = m_ScreenX0; + *tl_y = m_ScreenY0; + *br_x = m_ScreenX1; + *br_y = m_ScreenY1; + } + + virtual void LinesBegin() + { + dbg_assert(m_Drawing == 0, "called begin twice"); + m_Drawing = DRAWING_LINES; + SetColor(1,1,1,1); + } + + virtual void LinesEnd() + { + dbg_assert(m_Drawing == DRAWING_LINES, "called end without begin"); + Flush(); + m_Drawing = 0; + } + + virtual void LinesDraw(float x0, float y0, float x1, float y1) + { + dbg_assert(m_Drawing == DRAWING_LINES, "called draw without begin"); + + m_aVertices[m_NumVertices].m_Pos.x = x0; + m_aVertices[m_NumVertices].m_Pos.y = y0; + m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices].m_Color = m_aColor[0]; + + m_aVertices[m_NumVertices + 1].m_Pos.x = x1; + m_aVertices[m_NumVertices + 1].m_Pos.y = y1; + m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; + + AddVertices(2); + } + + + + virtual int UnloadTexture(int Index) + { + if(Index == m_InvalidTexture) + return 0; + + if(Index < 0) + return 0; + + glDeleteTextures(1, &m_aTextures[Index].tex); + m_aTextures[Index].next = m_FirstFreeTexture; + m_TextureMemoryUsage -= m_aTextures[Index].memsize; + m_FirstFreeTexture = Index; + return 0; + } + + + virtual int LoadTextureRaw(int Width, int Height, int Format, const void *pData, int StoreFormat, int Flags) + { + int mipmap = 1; + unsigned char *texdata = (unsigned char *)pData; + unsigned char *tmpdata = 0; + int oglformat = 0; + int store_oglformat = 0; + int tex = 0; + + /* don't waste memory on texture if we are stress testing */ + if(config.dbg_stress) + return m_InvalidTexture; + + /* grab texture */ + tex = m_FirstFreeTexture; + m_FirstFreeTexture = m_aTextures[tex].next; + m_aTextures[tex].next = -1; + + /* resample if needed */ + if(!(Flags&TEXLOAD_NORESAMPLE) && config.gfx_texture_quality==0) + { + if(Width > 16 && Height > 16 && Format == IMG_RGBA) + { + unsigned char *tmpdata; + int c = 0; + int x, y; + + tmpdata = (unsigned char *)mem_alloc(Width*Height*4, 1); + + Width/=2; + Height/=2; + + for(y = 0; y < Height; y++) + for(x = 0; x < Width; x++, c++) + { + tmpdata[c*4] = sample(Width*2, Height*2, texdata, x*2,y*2, 0); + tmpdata[c*4+1] = sample(Width*2, Height*2, texdata, x*2,y*2, 1); + tmpdata[c*4+2] = sample(Width*2, Height*2, texdata, x*2,y*2, 2); + tmpdata[c*4+3] = sample(Width*2, Height*2, texdata, x*2,y*2, 3); + } + texdata = tmpdata; + } + } + + oglformat = GL_RGBA; + if(Format == IMG_RGB) + oglformat = GL_RGB; + else if(Format == IMG_ALPHA) + oglformat = GL_ALPHA; + + /* upload texture */ + if(config.gfx_texture_compression) + { + store_oglformat = GL_COMPRESSED_RGBA_ARB; + if(StoreFormat == IMG_RGB) + store_oglformat = GL_COMPRESSED_RGB_ARB; + else if(StoreFormat == IMG_ALPHA) + store_oglformat = GL_COMPRESSED_ALPHA_ARB; + } + else + { + store_oglformat = GL_RGBA; + if(StoreFormat == IMG_RGB) + store_oglformat = GL_RGB; + else if(StoreFormat == IMG_ALPHA) + store_oglformat = GL_ALPHA; + } + + glGenTextures(1, &m_aTextures[tex].tex); + glBindTexture(GL_TEXTURE_2D, m_aTextures[tex].tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); + gluBuild2DMipmaps(GL_TEXTURE_2D, store_oglformat, Width, Height, oglformat, GL_UNSIGNED_BYTE, texdata); + + /* calculate memory usage */ + { + int pixel_size = 4; + if(StoreFormat == IMG_RGB) + pixel_size = 3; + else if(StoreFormat == IMG_ALPHA) + pixel_size = 1; + + m_aTextures[tex].memsize = Width*Height*pixel_size; + if(mipmap) + { + while(Width > 2 && Height > 2) + { + Width>>=1; + Height>>=1; + m_aTextures[tex].memsize += Width*Height*pixel_size; + } + } + } + + m_TextureMemoryUsage += m_aTextures[tex].memsize; + mem_free(tmpdata); + return tex; + } + + /* simple uncompressed RGBA loaders */ + virtual int LoadTexture(const char *pFilename, int StoreFormat, int Flags) + { + int l = strlen(pFilename); + int id; + IMAGE_INFO img; + + if(l < 3) + return -1; + if(LoadPNG(&img, pFilename)) + { + if (StoreFormat == IMG_AUTO) + StoreFormat = img.format; + + id = LoadTextureRaw(img.width, img.height, img.format, img.data, StoreFormat, Flags); + mem_free(img.data); + return id; + } + + return m_InvalidTexture; + } + + virtual int LoadPNG(IMAGE_INFO *pImg, const char *pFilename) + { + char aCompleteFilename[512]; + unsigned char *pBuffer; + png_t png; + + /* open file for reading */ + png_init(0,0); + + engine_getpath(aCompleteFilename, sizeof(aCompleteFilename), pFilename, IOFLAG_READ); + + if(png_open_file(&png, aCompleteFilename) != PNG_NO_ERROR) + { + dbg_msg("game/png", "failed to open file. filename='%s'", aCompleteFilename); + 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'", aCompleteFilename); + png_close_file(&png); + return 0; + } + + pBuffer = (unsigned char *)mem_alloc(png.width * png.height * png.bpp, 1); + png_get_data(&png, pBuffer); + png_close_file(&png); + + pImg->width = png.width; + pImg->height = png.height; + if(png.color_type == PNG_TRUECOLOR) + pImg->format = IMG_RGB; + else if(png.color_type == PNG_TRUECOLOR_ALPHA) + pImg->format = IMG_RGBA; + pImg->data = pBuffer; + return 1; + } + + void ScreenshotDirect(const char *filename) + { + /* fetch image data */ + int y; + int w = m_ScreenWidth; + int h = m_ScreenHeight; + unsigned char *pixel_data = (unsigned char *)mem_alloc(w*(h+1)*4, 1); + unsigned char *temp_row = pixel_data+w*h*4; + glReadPixels(0,0, w, h, GL_RGBA, 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*4, w*4); + mem_copy(pixel_data+y*w*4, pixel_data+(h-y-1)*w*4, w*4); + mem_copy(pixel_data+(h-y-1)*w*4, temp_row,w*4); + } + + /* find filename */ + { + char wholepath[1024]; + png_t png; + + engine_savepath(filename, wholepath, sizeof(wholepath)); + + /* 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_ALPHA, (unsigned char *)pixel_data); + png_close_file(&png); + } + + /* clean up */ + mem_free(pixel_data); + } + + virtual void TextureSet(int TextureID) + { + dbg_assert(m_Drawing == 0, "called Graphics()->TextureSet within begin"); + if(TextureID == -1) + { + glDisable(GL_TEXTURE_2D); + } + else + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, m_aTextures[TextureID].tex); + } + } + + virtual void Clear(float r, float g, float b) + { + glClearColor(r,g,b,0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + virtual void QuadsBegin() + { + dbg_assert(m_Drawing == 0, "called quads_begin twice"); + m_Drawing = DRAWING_QUADS; + + QuadsSetSubset(0,0,1,1); + QuadsSetRotation(0); + SetColor(1,1,1,1); + } + + virtual void QuadsEnd() + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_end without begin"); + Flush(); + m_Drawing = 0; + } + + virtual void QuadsSetRotation(float Angle) + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetRotation without begin"); + m_Rotation = Angle; + } + + virtual void SetColorVertex(int i, float r, float g, float b, float a) + { + dbg_assert(m_Drawing != 0, "called gfx_quads_setcolorvertex without begin"); + m_aColor[i].r = r; + m_aColor[i].g = g; + m_aColor[i].b = b; + m_aColor[i].a = a; + } + + virtual void SetColor(float r, float g, float b, float a) + { + dbg_assert(m_Drawing != 0, "called gfx_quads_setcolor without begin"); + SetColorVertex(0, r, g, b, a); + SetColorVertex(1, r, g, b, a); + SetColorVertex(2, r, g, b, a); + SetColorVertex(3, r, g, b, a); + } + + virtual void QuadsSetSubset(float tl_u, float tl_v, float br_u, float br_v) + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called Graphics()->QuadsSetSubset without begin"); + + m_aTexture[0].u = tl_u; m_aTexture[1].u = br_u; + m_aTexture[0].v = tl_v; m_aTexture[1].v = tl_v; + + m_aTexture[3].u = tl_u; m_aTexture[2].u = br_u; + m_aTexture[3].v = br_v; m_aTexture[2].v = br_v; + } + + virtual void QuadsSetSubsetFree( + float x0, float y0, float x1, float y1, + float x2, float y2, float x3, float y3) + { + m_aTexture[0].u = x0; m_aTexture[0].v = y0; + m_aTexture[1].u = x1; m_aTexture[1].v = y1; + m_aTexture[2].u = x2; m_aTexture[2].v = y2; + m_aTexture[3].u = x3; m_aTexture[3].v = y3; + } + + virtual void QuadsDraw(float x, float y, float w, float h) + { + QuadsDrawTL(x-w/2, y-h/2,w,h); + } + + virtual void QuadsDrawTL(float x, float y, float w, float h) + { + CPoint Center; + + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw without begin"); + + Center.x = x + w/2; + Center.y = y + h/2; + Center.z = 0; + + m_aVertices[m_NumVertices].m_Pos.x = x; + m_aVertices[m_NumVertices].m_Pos.y = y; + m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices].m_Color = m_aColor[0]; + Rotate(&Center, &m_aVertices[m_NumVertices].m_Pos); + + m_aVertices[m_NumVertices + 1].m_Pos.x = x+w; + m_aVertices[m_NumVertices + 1].m_Pos.y = y; + m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; + Rotate(&Center, &m_aVertices[m_NumVertices + 1].m_Pos); + + m_aVertices[m_NumVertices + 2].m_Pos.x = x + w; + m_aVertices[m_NumVertices + 2].m_Pos.y = y+h; + m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[2]; + m_aVertices[m_NumVertices + 2].m_Color = m_aColor[2]; + Rotate(&Center, &m_aVertices[m_NumVertices + 2].m_Pos); + + m_aVertices[m_NumVertices + 3].m_Pos.x = x; + m_aVertices[m_NumVertices + 3].m_Pos.y = y+h; + m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[3]; + m_aVertices[m_NumVertices + 3].m_Color = m_aColor[3]; + Rotate(&Center, &m_aVertices[m_NumVertices + 3].m_Pos); + + AddVertices(4); + } + + void QuadsDrawFreeform( + float x0, float y0, float x1, float y1, + float x2, float y2, float x3, float y3) + { + dbg_assert(m_Drawing == DRAWING_QUADS, "called quads_draw_freeform without begin"); + + m_aVertices[m_NumVertices].m_Pos.x = x0; + m_aVertices[m_NumVertices].m_Pos.y = y0; + m_aVertices[m_NumVertices].m_Tex = m_aTexture[0]; + m_aVertices[m_NumVertices].m_Color = m_aColor[0]; + + m_aVertices[m_NumVertices + 1].m_Pos.x = x1; + m_aVertices[m_NumVertices + 1].m_Pos.y = y1; + m_aVertices[m_NumVertices + 1].m_Tex = m_aTexture[1]; + m_aVertices[m_NumVertices + 1].m_Color = m_aColor[1]; + + m_aVertices[m_NumVertices + 2].m_Pos.x = x3; + m_aVertices[m_NumVertices + 2].m_Pos.y = y3; + m_aVertices[m_NumVertices + 2].m_Tex = m_aTexture[3]; + m_aVertices[m_NumVertices + 2].m_Color = m_aColor[3]; + + m_aVertices[m_NumVertices + 3].m_Pos.x = x2; + m_aVertices[m_NumVertices + 3].m_Pos.y = y2; + m_aVertices[m_NumVertices + 3].m_Tex = m_aTexture[2]; + m_aVertices[m_NumVertices + 3].m_Color = m_aColor[2]; + + AddVertices(4); + } + + virtual void QuadsText(float x, float y, float Size, float r, float g, float b, float a, const char *pText) + { + float startx = x; + + QuadsBegin(); + SetColor(r,g,b,a); + + while(*pText) + { + char c = *pText; + pText++; + + if(c == '\n') + { + x = startx; + y += Size; + } + else + { + QuadsSetSubset( + (c%16)/16.0f, + (c/16)/16.0f, + (c%16)/16.0f+1.0f/16.0f, + (c/16)/16.0f+1.0f/16.0f); + + QuadsDrawTL(x,y,Size,Size); + x += Size/2; + } + } + + QuadsEnd(); + } + + virtual bool Init() + { + /* Set all z to -5.0f */ + for(int i = 0; i < MAX_VERTICES; i++) + m_aVertices[i].m_Pos.z = -5.0f; + + /* init textures */ + m_FirstFreeTexture = 0; + for(int i = 0; i < MAX_TEXTURES; i++) + m_aTextures[i].next = i+1; + m_aTextures[MAX_TEXTURES-1].next = -1; + + /* set some default settings */ + glEnable(GL_BLEND); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glAlphaFunc(GL_GREATER, 0); + glEnable(GL_ALPHA_TEST); + glDepthMask(0); + + /* create null texture, will get id=0 */ + static const unsigned char aNullTextureData[] = { + 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, + }; + + m_InvalidTexture = LoadTextureRaw(4,4,IMG_RGBA,aNullTextureData,IMG_RGBA,TEXLOAD_NORESAMPLE); + dbg_msg("", "invalid texture id: %d %d", m_InvalidTexture, m_aTextures[m_InvalidTexture].tex); + + return true; + } +}; + +class CGraphics_SDL : public CGraphics_OpenGL +{ + SDL_Surface *m_pScreenSurface; + + int TryInit() + { + const SDL_VideoInfo *pInfo; + int Flags = SDL_OPENGL; + + m_ScreenWidth = config.gfx_screen_width; + m_ScreenHeight = config.gfx_screen_height; + + pInfo = SDL_GetVideoInfo(); + + /* set flags */ + Flags = SDL_OPENGL; + Flags |= SDL_GL_DOUBLEBUFFER; + Flags |= SDL_HWPALETTE; + if(config.dbg_resizable) + Flags |= SDL_RESIZABLE; + + if(pInfo->hw_available) + Flags |= SDL_HWSURFACE; + else + Flags |= SDL_SWSURFACE; + + if(pInfo->blit_hw) + Flags |= SDL_HWACCEL; + + if(config.gfx_fullscreen) + Flags |= SDL_FULLSCREEN; + + /* set gl attributes */ + if(config.gfx_fsaa_samples) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, config.gfx_fsaa_samples); + } + else + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); + } + + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, config.gfx_vsync); + + /* set caption */ + SDL_WM_SetCaption("Teeworlds", "Teeworlds"); + + /* create window */ + m_pScreenSurface = SDL_SetVideoMode(m_ScreenWidth, m_ScreenHeight, 0, Flags); + if(m_pScreenSurface == NULL) + { + dbg_msg("gfx", "unable to set video mode: %s", SDL_GetError()); + return -1; + } + + return 0; + } + + + int InitWindow() + { + if(TryInit() == 0) + return 0; + + /* try disabling fsaa */ + while(config.gfx_fsaa_samples) + { + config.gfx_fsaa_samples--; + + if(config.gfx_fsaa_samples) + dbg_msg("gfx", "lowering FSAA to %d and trying again", config.gfx_fsaa_samples); + else + dbg_msg("gfx", "disabling FSAA and trying again"); + + if(TryInit() == 0) + return 0; + } + + /* try lowering the resolution */ + if(config.gfx_screen_width != 640 || config.gfx_screen_height != 480) + { + dbg_msg("gfx", "setting resolution to 640x480 and trying again"); + config.gfx_screen_width = 640; + config.gfx_screen_height = 480; + + if(TryInit() == 0) + return 0; + } + + dbg_msg("gfx", "out of ideas. failed to init graphics"); + + return -1; + } + + +public: + CGraphics_SDL() + { + m_pScreenSurface = 0; + } + + virtual bool Init() + { + { + int Systems = SDL_INIT_VIDEO; + + if(config.snd_enable) + Systems |= SDL_INIT_AUDIO; + + if(config.cl_eventthread) + Systems |= SDL_INIT_EVENTTHREAD; + + if(SDL_Init(Systems) < 0) + { + dbg_msg("gfx", "unable to init SDL: %s", SDL_GetError()); + return -1; + } + } + + atexit(SDL_Quit); + + #ifdef CONF_FAMILY_WINDOWS + if(!getenv("SDL_VIDEO_WINDOW_POS") && !getenv("SDL_VIDEO_CENTERED")) + putenv("SDL_VIDEO_WINDOW_POS=8,27"); + #endif + + if(InitWindow() != 0) + return -1; + + SDL_ShowCursor(0); + + CGraphics_OpenGL::Init(); + + MapScreen(0,0,config.gfx_screen_width, config.gfx_screen_height); + + /* init input */ + inp_init(); + + /* font init */ + gfx_font_init(); + + return 0; + } + + virtual void Shutdown() + { + /* TODO: SDL, is this correct? */ + SDL_Quit(); + } + + virtual void Minimize() + { + SDL_WM_IconifyWindow(); + } + + virtual void Maximize() + { + /* TODO: SDL */ + } + + virtual int WindowActive() + { + return SDL_GetAppState()&SDL_APPINPUTFOCUS; + } + + virtual int WindowOpen() + { + return SDL_GetAppState()&SDL_APPACTIVE; + + } + + virtual void TakeScreenshot() + { + m_DoScreenshot = true; + } + + virtual void Swap() + { + if(m_DoScreenshot) + { + /* find filename */ + char filename[128]; + static int index = 1; + + for(; index < 1000; index++) + { + IOHANDLE io; + str_format(filename, sizeof(filename), "screenshots/screenshot%04d.png", index); + io = engine_openfile(filename, IOFLAG_READ); + if(io) + io_close(io); + else + break; + } + + ScreenshotDirect(filename); + m_DoScreenshot = false; + } + + { + static PERFORMACE_INFO pscope = {"glfwSwapBuffers", 0}; + perf_start(&pscope); + SDL_GL_SwapBuffers(); + perf_end(); + } + + if(config.gfx_finish) + glFinish(); + } +}; + +extern IEngineGraphics *CreateEngineGraphics() { return new CGraphics_SDL(); } diff --git a/src/engine/client/ec_gfx_text.c b/src/engine/client/ec_gfx_text.cpp index de40e391..d17d1bed 100644 --- a/src/engine/client/ec_gfx_text.c +++ b/src/engine/client/ec_gfx_text.cpp @@ -1,7 +1,9 @@ #include <base/system.h> #include <string.h> #include <engine/e_client_interface.h> +#include <engine/client/graphics.h> +extern IEngineGraphics *Graphics(); #ifdef CONF_PLATFORM_MACOSX #include <OpenGL/gl.h> @@ -138,8 +140,7 @@ typedef struct FONT static int font_get_index(int pixelsize) { - int i; - for(i = 0; i < NUM_FONT_SIZES; i++) + for(unsigned i = 0; i < NUM_FONT_SIZES; i++) { if(font_sizes[i] >= pixelsize) return i; @@ -150,8 +151,7 @@ static int font_get_index(int pixelsize) FONT *gfx_font_load(const char *filename) { - int i; - FONT *font = mem_alloc(sizeof(FONT), 1); + FONT *font = (FONT *)mem_alloc(sizeof(FONT), 1); mem_zero(font, sizeof(*font)); str_copy(font->filename, filename, sizeof(font->filename)); @@ -162,7 +162,7 @@ FONT *gfx_font_load(const char *filename) return NULL; } - for(i = 0; i < NUM_FONT_SIZES; i++) + for(unsigned i = 0; i < NUM_FONT_SIZES; i++) font->sizes[i].font_size = -1; return font; @@ -484,6 +484,7 @@ static float font_kerning(FONT *font, int left, int right) return (kerning.x>>6); } + void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) { FONT *font = cursor->font; @@ -503,10 +504,10 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) float size = cursor->font_size; /* to correct coords, convert to screen coords, round, and convert back */ - gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); + Graphics()->GetScreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1); - fake_to_screen_x = (gfx_screenwidth()/(screen_x1-screen_x0)); - fake_to_screen_y = (gfx_screenheight()/(screen_y1-screen_y0)); + fake_to_screen_x = (Graphics()->ScreenWidth()/(screen_x1-screen_x0)); + fake_to_screen_y = (Graphics()->ScreenHeight()/(screen_y1-screen_y0)); actual_x = cursor->x * fake_to_screen_x; actual_y = cursor->y * fake_to_screen_y; @@ -554,11 +555,11 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) else glBindTexture(GL_TEXTURE_2D, sizedata->textures[0]); - gfx_quads_begin(); + Graphics()->QuadsBegin(); if (i == 0) - gfx_setcolor(0.0f, 0.0f, 0.0f, 0.3f*text_a); + Graphics()->SetColor(0.0f, 0.0f, 0.0f, 0.3f*text_a); else - gfx_setcolor(text_r, text_g, text_b, text_a); + Graphics()->SetColor(text_r, text_g, text_b, text_a); } while(current < end) @@ -629,8 +630,8 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) { if(cursor->flags&TEXTFLAG_RENDER) { - gfx_quads_setsubset(chr->uvs[0], chr->uvs[1], chr->uvs[2], chr->uvs[3]); - gfx_quads_drawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size); + Graphics()->QuadsSetSubset(chr->uvs[0], chr->uvs[1], chr->uvs[2], chr->uvs[3]); + Graphics()->QuadsDrawTL(draw_x+chr->offset_x*size, draw_y+chr->offset_y*size, chr->width*size, chr->height*size); } advance = chr->advance_x + font_kerning(font, character, nextcharacter)/size; @@ -658,7 +659,7 @@ void gfx_text_ex(TEXT_CURSOR *cursor, const char *text, int length) } if(cursor->flags&TEXTFLAG_RENDER) - gfx_quads_end(); + Graphics()->QuadsEnd(); } cursor->x = draw_x; diff --git a/src/engine/client/ec_inp.c b/src/engine/client/ec_inp.cpp index 495614d6..cf956471 100644 --- a/src/engine/client/ec_inp.c +++ b/src/engine/client/ec_inp.cpp @@ -5,6 +5,7 @@ #include <base/system.h> #include <engine/e_client_interface.h> #include <engine/e_config.h> +#include <engine/client/graphics.h> static struct { @@ -20,6 +21,9 @@ static int input_grabbed = 0; static unsigned int last_release = 0; static unsigned int release_delta = -1; +// TODO: Refactor: Remove this +extern IEngineGraphics *Graphics(); + void inp_mouse_relative(int *x, int *y) { int nx = 0, ny = 0; @@ -32,8 +36,8 @@ void inp_mouse_relative(int *x, int *y) if(input_grabbed) { SDL_GetMouseState(&nx,&ny); - SDL_WarpMouse(gfx_screenwidth()/2,gfx_screenheight()/2); - nx -= gfx_screenwidth()/2; ny -= gfx_screenheight()/2; + SDL_WarpMouse(Graphics()->ScreenWidth()/2,Graphics()->ScreenHeight()/2); + nx -= Graphics()->ScreenWidth()/2; ny -= Graphics()->ScreenHeight()/2; } } @@ -138,10 +142,10 @@ void inp_update() { int i; - if(input_grabbed && !gfx_window_active()) + if(input_grabbed && !Graphics()->WindowActive()) inp_mouse_mode_absolute(); - /*if(!input_grabbed && gfx_window_active()) + /*if(!input_grabbed && Graphics()->WindowActive()) inp_mouse_mode_relative();*/ /* clear and begin count on the other one */ diff --git a/src/engine/client/ec_snd.c b/src/engine/client/ec_snd.cpp index ac41ec59..3baea982 100644 --- a/src/engine/client/ec_snd.c +++ b/src/engine/client/ec_snd.cpp @@ -1,12 +1,15 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ #include <base/system.h> #include <engine/e_client_interface.h> +#include <engine/client/graphics.h> #include <engine/e_config.h> #include <engine/e_engine.h> #include "SDL.h" -#include <engine/external/wavpack/wavpack.h> +extern "C" { // wavpack + #include <engine/external/wavpack/wavpack.h> +} #include <stdio.h> #include <stdlib.h> #include <math.h> @@ -140,7 +143,6 @@ static int iabs(int i) static void mix(short *final_out, unsigned frames) { int mix_buffer[MAX_FRAMES*2] = {0}; - int i, s; int master_vol; /* aquire lock while we are mixing */ @@ -148,7 +150,7 @@ static void mix(short *final_out, unsigned frames) master_vol = sound_volume; - for(i = 0; i < NUM_VOICES; i++) + for(unsigned i = 0; i < NUM_VOICES; i++) { if(voices[i].snd) { @@ -160,7 +162,7 @@ static void mix(short *final_out, unsigned frames) 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; + unsigned end = v->snd->num_frames-v->tick; int rvol = v->channel->vol; int lvol = v->channel->vol; @@ -202,7 +204,7 @@ static void mix(short *final_out, unsigned frames) } /* process all frames */ - for(s = 0; s < end; s++) + for(unsigned s = 0; s < end; s++) { *out++ += (*in_l)*lvol; *out++ += (*in_r)*rvol; @@ -225,7 +227,7 @@ static void mix(short *final_out, unsigned frames) { /* clamp accumulated values */ /* TODO: this seams slow */ - for(i = 0; i < frames; i++) + for(unsigned i = 0; i < frames; i++) { int j = i<<1; int vl = ((mix_buffer[j]*master_vol)/101)>>8; @@ -281,12 +283,16 @@ int snd_init() return 0; } +// TODO: Refactor: Remove this +extern IEngineGraphics *Graphics(); + + int snd_update() { /* update volume */ int wanted_volume = config.snd_volume; - if(!gfx_window_active() && config.snd_nonactive_mute) + if(!Graphics()->WindowActive() && config.snd_nonactive_mute) wanted_volume = 0; if(wanted_volume != sound_volume) @@ -332,7 +338,7 @@ static void rate_convert(int sid) /* 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); + new_data = (short *)mem_alloc(num_frames*snd->channels*sizeof(short), 1); for(i = 0; i < num_frames; i++) { diff --git a/src/engine/client/ec_srvbrowse.c b/src/engine/client/ec_srvbrowse.cpp index 4a85e778..1b04937a 100644 --- a/src/engine/client/ec_srvbrowse.c +++ b/src/engine/client/ec_srvbrowse.cpp @@ -11,7 +11,7 @@ #include <string.h> #include <stdlib.h> -extern NETCLIENT *net; +extern CNetClient m_NetClient; /* ------ server browse ---- */ @@ -150,7 +150,7 @@ static void client_serverbrowse_filter() if(sorted_serverlist) mem_free(sorted_serverlist); num_sorted_servers_capacity = num_servers; - sorted_serverlist = mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); + sorted_serverlist = (int *)mem_alloc(num_sorted_servers_capacity*sizeof(int), 1); } /* filter the servers */ @@ -401,7 +401,7 @@ SERVERENTRY *client_serverbrowse_add(NETADDR *addr) { SERVERENTRY **newlist; num_server_capacity += 100; - newlist = mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); + newlist = (SERVERENTRY **)mem_alloc(num_server_capacity*sizeof(SERVERENTRY*), 1); mem_copy(newlist, serverlist, num_servers*sizeof(SERVERENTRY*)); mem_free(serverlist); serverlist = newlist; @@ -506,28 +506,28 @@ void client_serverbrowse_refresh(int type) if(type == BROWSETYPE_LAN) { - unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1]; - NETCHUNK packet; + unsigned char Buffer[sizeof(SERVERBROWSE_GETINFO)+1]; + CNetChunk Packet; int i; - mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); - buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token; + mem_copy(Buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); + Buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token; - 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.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(buffer); - packet.data = buffer; + Packet.m_ClientID = -1; + mem_zero(&Packet, sizeof(Packet)); + Packet.m_Address.ip[0] = 255; + Packet.m_Address.ip[1] = 255; + Packet.m_Address.ip[2] = 255; + Packet.m_Address.ip[3] = 255; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(Buffer); + Packet.m_pData = Buffer; broadcast_time = time_get(); for(i = 8303; i <= 8310; i++) { - packet.address.port = i; - netclient_send(net, &packet); + Packet.m_Address.port = i; + m_NetClient.Send(&Packet); } if(config.debug) @@ -537,8 +537,7 @@ void client_serverbrowse_refresh(int type) need_refresh = 1; else if(type == BROWSETYPE_FAVORITES) { - int i; - for(i = 0; i < num_favorite_servers; i++) + for(int i = 0; i < num_favorite_servers; i++) client_serverbrowse_set(&favorite_servers[i], BROWSESET_FAV_ADD, -1, 0); } } @@ -546,7 +545,7 @@ void client_serverbrowse_refresh(int type) static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry) { /*unsigned char buffer[sizeof(SERVERBROWSE_GETINFO)+1];*/ - NETCHUNK p; + CNetChunk Packet; if(config.debug) { @@ -558,17 +557,17 @@ static void client_serverbrowse_request_impl(NETADDR *addr, SERVERENTRY *entry) /*mem_copy(buffer, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)); buffer[sizeof(SERVERBROWSE_GETINFO)] = current_token;*/ - p.client_id = -1; - p.address = *addr; - p.flags = NETSENDFLAG_CONNLESS; + Packet.m_ClientID = -1; + Packet.m_Address = *addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; /*p.data_size = sizeof(buffer); p.data = buffer; netclient_send(net, &p);*/ /* send old requtest style aswell */ - p.data_size = sizeof(SERVERBROWSE_OLD_GETINFO); - p.data = SERVERBROWSE_OLD_GETINFO; - netclient_send(net, &p); + Packet.m_DataSize = sizeof(SERVERBROWSE_OLD_GETINFO); + Packet.m_pData = SERVERBROWSE_OLD_GETINFO; + m_NetClient.Send(&Packet); if(entry) entry->request_time = time_get(); @@ -591,16 +590,16 @@ void client_serverbrowse_update() if(need_refresh && !mastersrv_refreshing()) { NETADDR addr; - NETCHUNK p; + CNetChunk Packet; int i; need_refresh = 0; - mem_zero(&p, sizeof(p)); - p.client_id = -1; - p.flags = NETSENDFLAG_CONNLESS; - p.data_size = sizeof(SERVERBROWSE_GETLIST); - p.data = SERVERBROWSE_GETLIST; + mem_zero(&Packet, sizeof(Packet)); + Packet.m_ClientID = -1; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETLIST); + Packet.m_pData = SERVERBROWSE_GETLIST; for(i = 0; i < MAX_MASTERSERVERS; i++) { @@ -608,8 +607,8 @@ void client_serverbrowse_update() if(!addr.ip[0] && !addr.ip[1] && !addr.ip[2] && !addr.ip[3]) continue; - p.address = addr; - netclient_send(net, &p); + Packet.m_Address = addr; + m_NetClient.Send(&Packet); } if(config.debug) diff --git a/src/engine/e_client_interface.h b/src/engine/e_client_interface.h index 7ec5c93d..079eabca 100644 --- a/src/engine/e_client_interface.h +++ b/src/engine/e_client_interface.h @@ -2,10 +2,6 @@ #ifndef ENGINE_CLIENT_INTERFACE_H #define ENGINE_CLIENT_INTERFACE_H -#ifdef __cplusplus -extern "C" { -#endif - #include "e_if_other.h" #include "e_if_client.h" #include "e_if_snd.h" @@ -17,8 +13,4 @@ extern "C" { #include "e_console.h" #include "e_config.h" -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_common_interface.h b/src/engine/e_common_interface.h index 498bb6b4..9c95b48b 100644 --- a/src/engine/e_common_interface.h +++ b/src/engine/e_common_interface.h @@ -2,15 +2,7 @@ #ifndef ENGINE_COMMON_INTERFACE_H #define ENGINE_COMMON_INTERFACE_H -#ifdef __cplusplus -extern "C" { -#endif - #include "e_if_other.h" #include "e_if_msg.h" -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_compression.c b/src/engine/e_compression.cpp index 6a534feb..f4d6e0c0 100644 --- a/src/engine/e_compression.c +++ b/src/engine/e_compression.cpp @@ -84,65 +84,3 @@ long intpack_compress(const void *src_, int size, void *dst_) return (long)(dst-(unsigned char *)dst_); } - -/* */ -long zerobit_compress(const void *src_, int size, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *dst = (unsigned char *)dst_; - - while(size) - { - unsigned char bit = 0x80; - unsigned char mask = 0; - int dst_move = 1; - int chunk = size < 8 ? size : 8; - int b; - size -= chunk; - - for(b = 0; b < chunk; b++, bit>>=1) - { - if(*src) - { - dst[dst_move] = *src; - mask |= bit; - dst_move++; - } - - src++; - } - - *dst = mask; - dst += dst_move; - } - - return (long)(dst-(unsigned char *)dst_); -} - -long zerobit_decompress(const void *src_, int size, void *dst_) -{ - unsigned char *src = (unsigned char *)src_; - unsigned char *dst = (unsigned char *)dst_; - unsigned char *end = src + size; - - while(src < end) - { - unsigned char bit = 0x80; - unsigned char mask = *src++; - int b; - - for(b = 0; b < 8; b++, bit>>=1) - { - if(mask&bit) - *dst++ = *src++; - else - *dst++ = 0; - } - - if(src > end) - return -1; - } - - return (long)(dst-(unsigned char *)dst_); -} - diff --git a/src/engine/e_compression.h b/src/engine/e_compression.h index 39ffd596..be5bf78f 100644 --- a/src/engine/e_compression.h +++ b/src/engine/e_compression.h @@ -5,7 +5,3 @@ unsigned char *vint_pack(unsigned char *dst, int i); const unsigned char *vint_unpack(const unsigned char *src, int *inout); long intpack_compress(const void *src, int size, void *dst); long intpack_decompress(const void *src, int size, void *dst); - -/* zerobit packing */ -long zerobit_compress(const void *src, int size, void *dst); -long zerobit_decompress(const void *src, int size, void *dst); diff --git a/src/engine/e_config.c b/src/engine/e_config.cpp index 67a4c81a..67a4c81a 100644 --- a/src/engine/e_config.c +++ b/src/engine/e_config.cpp diff --git a/src/engine/e_config.h b/src/engine/e_config.h index 965e08f0..6ca7a8ee 100644 --- a/src/engine/e_config.h +++ b/src/engine/e_config.h @@ -2,10 +2,6 @@ #ifndef _CONFIG_H #define _CONFIG_H -#ifdef __cplusplus -extern "C"{ -#endif - typedef struct { #define MACRO_CONFIG_INT(name,def,min,max,save,desc) int name; @@ -45,8 +41,4 @@ typedef void (*CONFIG_STR_SETTER)(CONFIGURATION *c, const char *str); #undef MACRO_CONFIG_INT #undef MACRO_CONFIG_STR -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_console.c b/src/engine/e_console.cpp index 5c0f37f3..c641289d 100644 --- a/src/engine/e_console.c +++ b/src/engine/e_console.cpp @@ -151,7 +151,7 @@ static int console_parse_args(PARSE_RESULT *result, const char *format) return error; } -const char *console_arg_string(void *res, int index) +const char *console_arg_string(void *res, unsigned index) { PARSE_RESULT *result = (PARSE_RESULT *)res; if (index < 0 || index >= result->num_args) @@ -159,7 +159,7 @@ const char *console_arg_string(void *res, int index) return result->args[index]; } -int console_arg_int(void *res, int index) +int console_arg_int(void *res, unsigned index) { PARSE_RESULT *result = (PARSE_RESULT *)res; if (index < 0 || index >= result->num_args) @@ -167,7 +167,7 @@ int console_arg_int(void *res, int index) return atoi(result->args[index]); } -float console_arg_float(void *res, int index) +float console_arg_float(void *res, unsigned index) { PARSE_RESULT *result = (PARSE_RESULT *)res; if (index < 0 || index >= result->num_args) @@ -354,7 +354,7 @@ struct EXECFILE void console_execute_file(const char *filename) { static struct EXECFILE *first = 0; - struct EXECFILE this; + struct EXECFILE this_file; struct EXECFILE *cur; struct EXECFILE *prev; @@ -365,9 +365,9 @@ void console_execute_file(const char *filename) /* push this one to the stack */ prev = first; - this.filename = filename; - this.next = first; - first = &this; + this_file.filename = filename; + this_file.next = first; + first = &this_file; /* execute file */ console_execute_file_real(filename); diff --git a/src/engine/e_console.h b/src/engine/e_console.h index b37931b2..a45bac10 100644 --- a/src/engine/e_console.h +++ b/src/engine/e_console.h @@ -1,10 +1,6 @@ #ifndef _CONSOLE_H #define _CONSOLE_H -#ifdef __cplusplus -extern "C"{ -#endif - typedef void (*CONSOLE_CALLBACK)(void *result, void *user_data); typedef void (*CONSOLE_CHAIN_CALLBACK)(void *result, void *user_data, CONSOLE_CALLBACK cb, void *cbuser); @@ -42,15 +38,11 @@ void console_register_print_callback(void (*callback)(const char *, void *user_d int console_result_int(void *result, int index, int *i); int console_result_float(void *result, int index, float *f);*/ -const char *console_arg_string(void *result, int index); -int console_arg_int(void *result, int index); -float console_arg_float(void *result, int index); +const char *console_arg_string(void *result, unsigned index); +int console_arg_int(void *result, unsigned index); +float console_arg_float(void *result, unsigned index); int console_arg_num(void *result); #define MACRO_REGISTER_COMMAND(name, params, flags, func, ptr, help) { static COMMAND cmd = { name, params, flags, func, ptr, help, 0x0}; console_register(&cmd); } -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_datafile.c b/src/engine/e_datafile.cpp index 0317f9d0..0317f9d0 100644 --- a/src/engine/e_datafile.c +++ b/src/engine/e_datafile.cpp diff --git a/src/engine/e_demorec.c b/src/engine/e_demorec.cpp index e66173e4..1bab1b8d 100644 --- a/src/engine/e_demorec.c +++ b/src/engine/e_demorec.cpp @@ -1,4 +1,3 @@ - #include <base/system.h> #include "e_demorec.h" #include "e_memheap.h" @@ -14,7 +13,7 @@ static const unsigned char header_marker[8] = {'T', 'W', 'D', 'E', 'M', 'O', 0, /* Record */ static int record_lasttickmarker = -1; static int record_lastkeyframe; -static unsigned char record_lastsnapshotdata[MAX_SNAPSHOT_SIZE]; +static unsigned char record_lastsnapshotdata[CSnapshot::MAX_SIZE]; int demorec_isrecording() { return record_file != 0; } @@ -121,7 +120,7 @@ static void demorec_record_write(int type, const void *data, int size) while(size&3) buffer2[size++] = 0; size = intpack_compress(buffer2, size, buffer); /* buffer2 -> buffer */ - size = netcommon_compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */ + size = CNetBase::Compress(buffer, size, buffer2, sizeof(buffer2)); /* buffer -> buffer2 */ chunk[0] = ((type&0x3)<<5); @@ -166,13 +165,13 @@ void demorec_record_snapshot(int tick, const void *data, int size) else { /* create delta, prepend tick */ - char delta_data[MAX_SNAPSHOT_SIZE+sizeof(int)]; + char delta_data[CSnapshot::MAX_SIZE+sizeof(int)]; int delta_size; /* write tickmarker */ demorec_record_write_tickmarker(tick, 0); - delta_size = snapshot_create_delta((SNAPSHOT*)record_lastsnapshotdata, (SNAPSHOT*)data, &delta_data); + delta_size = CSnapshot::CreateDelta((CSnapshot*)record_lastsnapshotdata, (CSnapshot*)data, &delta_data); if(delta_size) { /* record delta */ @@ -217,7 +216,7 @@ static DEMOREC_PLAYCALLBACK play_callback_message = 0; static KEYFRAME *keyframes = 0; static DEMOREC_PLAYBACKINFO playbackinfo; -static unsigned char playback_lastsnapshotdata[MAX_SNAPSHOT_SIZE]; +static unsigned char playback_lastsnapshotdata[CSnapshot::MAX_SIZE]; static int playback_lastsnapshotdata_size = -1; @@ -316,7 +315,7 @@ static void scan_file() KEYFRAME_SEARCH *key; /* save the position */ - key = memheap_allocate(heap, sizeof(KEYFRAME_SEARCH)); + key = (KEYFRAME_SEARCH *)memheap_allocate(heap, sizeof(KEYFRAME_SEARCH)); key->frame.filepos = current_pos; key->frame.tick = chunk_tick; key->next = 0; @@ -349,10 +348,10 @@ static void scan_file() static void do_tick() { - static char compresseddata[MAX_SNAPSHOT_SIZE]; - static char decompressed[MAX_SNAPSHOT_SIZE]; - static char data[MAX_SNAPSHOT_SIZE]; - int chunk_size, chunk_type, chunk_tick; + static char compresseddata[CSnapshot::MAX_SIZE]; + static char decompressed[CSnapshot::MAX_SIZE]; + static char data[CSnapshot::MAX_SIZE]; + int chunk_type, chunk_tick, chunk_size; int data_size; int got_snapshot = 0; @@ -374,7 +373,7 @@ static void do_tick() /* read the chunk */ if(chunk_size) { - if(io_read(play_file, compresseddata, chunk_size) != chunk_size) + if(io_read(play_file, compresseddata, chunk_size) != (unsigned)chunk_size) { /* stop on error or eof */ dbg_msg("demorec", "error reading chunk"); @@ -382,7 +381,7 @@ static void do_tick() break; } - data_size = netcommon_decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed)); + data_size = CNetBase::Decompress(compresseddata, chunk_size, decompressed, sizeof(decompressed)); if(data_size < 0) { /* stop on error or eof */ @@ -404,11 +403,11 @@ static void do_tick() if(chunk_type == CHUNKTYPE_DELTA) { /* process delta snapshot */ - static char newsnap[MAX_SNAPSHOT_SIZE]; + static char newsnap[CSnapshot::MAX_SIZE]; got_snapshot = 1; - data_size = snapshot_unpack_delta((SNAPSHOT*)playback_lastsnapshotdata, (SNAPSHOT*)newsnap, data, data_size); + data_size = CSnapshot::UnpackDelta((CSnapshot*)playback_lastsnapshotdata, (CSnapshot*)newsnap, data, data_size); if(data_size >= 0) { @@ -625,7 +624,7 @@ int demorec_playback_update() return 0; } -int demorec_playback_stop(const char *filename) +int demorec_playback_stop() { if(!play_file) return -1; @@ -637,3 +636,5 @@ int demorec_playback_stop(const char *filename) keyframes = 0; return 0; } + + diff --git a/src/engine/e_demorec.h b/src/engine/e_demorec.h index 482debb4..9716b463 100644 --- a/src/engine/e_demorec.h +++ b/src/engine/e_demorec.h @@ -2,10 +2,6 @@ #ifndef _DEMOREC_H #define _DEMOREC_H -#ifdef __cplusplus -extern "C"{ -#endif - typedef struct DEMOREC_HEADER { char marker[8]; @@ -70,8 +66,4 @@ const DEMOREC_PLAYBACKINFO *demorec_playback_info(); int demorec_isplaying(); int demorec_playback_stop(); -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_engine.c b/src/engine/e_engine.cpp index 7afbb2ce..4475478a 100644 --- a/src/engine/e_engine.c +++ b/src/engine/e_engine.cpp @@ -25,7 +25,7 @@ static void con_dbg_dumpmem(void *result, void *user_data) static void con_dbg_lognetwork(void *result, void *user_data) { - netcommon_openlog("network_sent.dat", "network_recv.dat"); + CNetBase::OpenLog("network_sent.dat", "network_recv.dat"); } @@ -55,7 +55,7 @@ void engine_init(const char *appname) /* init the network */ net_init(); - netcommon_init(); + CNetBase::Init(); /* create storage location */ { @@ -246,7 +246,7 @@ static IOHANDLE config_file = 0; int engine_config_write_start() { - config_save("settings.cfg"); + config_save(); config_file = engine_openfile("settings.cfg", IOFLAG_WRITE); if(config_file == 0) return -1; diff --git a/src/engine/e_huffman.c b/src/engine/e_huffman.cpp index 43914010..43914010 100644 --- a/src/engine/e_huffman.c +++ b/src/engine/e_huffman.cpp diff --git a/src/engine/e_huffman.h b/src/engine/e_huffman.h index c4e20223..635c74a1 100644 --- a/src/engine/e_huffman.h +++ b/src/engine/e_huffman.h @@ -1,10 +1,6 @@ #ifndef __HUFFMAN_HEADER__ #define __HUFFMAN_HEADER__ -#ifdef __cplusplus -extern "C" { -#endif - enum { HUFFMAN_EOF_SYMBOL = 256, @@ -84,8 +80,4 @@ int huffman_compress(HUFFMAN_STATE *huff, const void *input, int input_size, voi */ int huffman_decompress(HUFFMAN_STATE *huff, const void *input, int input_size, void *output, int output_size); -#ifdef __cplusplus -} -#endif - #endif /* __HUFFMAN_HEADER__ */ diff --git a/src/engine/e_if_other.h b/src/engine/e_if_other.h index 5c839750..3fbbd81e 100644 --- a/src/engine/e_if_other.h +++ b/src/engine/e_if_other.h @@ -69,11 +69,12 @@ void perf_init(); void perf_next(); void perf_start(PERFORMACE_INFO *info); void perf_end(); -void perf_dump(); +void perf_dump(PERFORMACE_INFO *top); int gfx_init(); void gfx_shutdown(); void gfx_swap(); + int gfx_window_active(); int gfx_window_open(); diff --git a/src/engine/e_jobs.c b/src/engine/e_jobs.cpp index e87b395a..e87b395a 100644 --- a/src/engine/e_jobs.c +++ b/src/engine/e_jobs.cpp diff --git a/src/engine/e_keynames.c b/src/engine/e_keynames.cpp index c81744b9..c81744b9 100644 --- a/src/engine/e_keynames.c +++ b/src/engine/e_keynames.cpp diff --git a/src/engine/e_linereader.c b/src/engine/e_linereader.cpp index 57ba9a85..57ba9a85 100644 --- a/src/engine/e_linereader.c +++ b/src/engine/e_linereader.cpp diff --git a/src/engine/e_map.c b/src/engine/e_map.cpp index a2048310..a2048310 100644 --- a/src/engine/e_map.c +++ b/src/engine/e_map.cpp diff --git a/src/engine/e_memheap.c b/src/engine/e_memheap.cpp index 7589e2c1..fe157e86 100644 --- a/src/engine/e_memheap.c +++ b/src/engine/e_memheap.cpp @@ -1,18 +1,18 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ #include <base/system.h> -typedef struct CHUNK_t +struct CHUNK { char *memory; char *current; char *end; - struct CHUNK_t *next; -} CHUNK; + CHUNK *next; +}; -typedef struct +struct HEAP { CHUNK *current; -} HEAP; +}; /* how large each chunk should be */ static const int chunksize = 1024*64; diff --git a/src/engine/e_memheap.h b/src/engine/e_memheap.h index af3c0b29..b4391ec7 100644 --- a/src/engine/e_memheap.h +++ b/src/engine/e_memheap.h @@ -1,6 +1,6 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -typedef struct HEAP_t HEAP; +struct HEAP; HEAP *memheap_create(); void memheap_destroy(HEAP *heap); void *memheap_allocate(HEAP *heap, unsigned int size); diff --git a/src/engine/e_msg.c b/src/engine/e_msg.cpp index f9efc2bf..999a0ff0 100644 --- a/src/engine/e_msg.c +++ b/src/engine/e_msg.cpp @@ -3,17 +3,17 @@ #include "e_packer.h" /* message packing */ -static PACKER msg_packer; +static CPacker msg_packer; static MSG_INFO pack_info; static int packer_failed = 0; -void msg_pack_int(int i) { packer_add_int(&msg_packer, i); } -void msg_pack_string(const char *p, int limit) { packer_add_string(&msg_packer, p, limit); } -void msg_pack_raw(const void *data, int size) { packer_add_raw(&msg_packer, (const unsigned char *)data, size); } +void msg_pack_int(int i) { msg_packer.AddInt(i); } +void msg_pack_string(const char *p, int limit) { msg_packer.AddString(p, limit); } +void msg_pack_raw(const void *data, int size) { msg_packer.AddRaw((const unsigned char *)data, size); } void msg_pack_start_system(int msg, int flags) { - packer_reset(&msg_packer); + msg_packer.Reset(); pack_info.msg = (msg<<1)|1; pack_info.flags = flags; packer_failed = 0; @@ -23,7 +23,7 @@ void msg_pack_start_system(int msg, int flags) void msg_pack_start(int msg, int flags) { - packer_reset(&msg_packer); + msg_packer.Reset(); pack_info.msg = msg<<1; pack_info.flags = flags; packer_failed = 0; @@ -33,7 +33,7 @@ void msg_pack_start(int msg, int flags) void msg_pack_end() { - if(msg_packer.error) + if(msg_packer.Error()) { packer_failed = 1; pack_info.size = 0; @@ -41,8 +41,8 @@ void msg_pack_end() } else { - pack_info.size = packer_size(&msg_packer); - pack_info.data = packer_data(&msg_packer); + pack_info.size = msg_packer.Size(); + pack_info.data = msg_packer.Data(); } } @@ -54,17 +54,17 @@ const MSG_INFO *msg_get_info() } /* message unpacking */ -static UNPACKER msg_unpacker; +static CUnpacker msg_unpacker; int msg_unpack_start(const void *data, int data_size, int *system) { int msg; - unpacker_reset(&msg_unpacker, (const unsigned char *)data, data_size); + msg_unpacker.Reset((const unsigned char *)data, data_size); msg = msg_unpack_int(); *system = msg&1; return msg>>1; } -int msg_unpack_int() { return unpacker_get_int(&msg_unpacker); } -const char *msg_unpack_string() { return unpacker_get_string(&msg_unpacker); } -const unsigned char *msg_unpack_raw(int size) { return unpacker_get_raw(&msg_unpacker, size); } -int msg_unpack_error() { return msg_unpacker.error; } +int msg_unpack_int() { return msg_unpacker.GetInt(); } +const char *msg_unpack_string() { return msg_unpacker.GetString(); } +const unsigned char *msg_unpack_raw(int size) { return msg_unpacker.GetRaw(size); } +int msg_unpack_error() { return msg_unpacker.Error(); } diff --git a/src/engine/e_network.c b/src/engine/e_network.c deleted file mode 100644 index 3c32de1d..00000000 --- a/src/engine/e_network.c +++ /dev/null @@ -1,351 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <base/system.h> - -#include <string.h> /* strlen */ - -#include "e_config.h" -#include "e_engine.h" -#include "e_network.h" -#include "e_network_internal.h" -#include "e_huffman.h" - -void recvinfo_clear(NETRECVINFO *info) -{ - info->valid = 0; -} - -void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid) -{ - info->addr = *addr; - info->conn = conn; - info->client_id = cid; - info->current_chunk = 0; - info->valid = 1; -} - - -int seq_in_backroom(int seq, int ack) -{ - int bottom = (ack-NET_MAX_SEQUENCE/2); - if(bottom < 0) - { - if(seq <= ack) - return 1; - if(seq >= (bottom + NET_MAX_SEQUENCE)) - return 1; - } - else - { - if(seq <= ack && seq >= bottom) - return 1; - } - - return 0; -} - -/* TODO: rename this function */ -int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk) -{ - NETCHUNKHEADER header; - unsigned char *end = info->data.chunk_data + info->data.data_size; - int i; - - while(1) - { - unsigned char *data = info->data.chunk_data; - - /* check for old data to unpack */ - if(!info->valid || info->current_chunk >= info->data.num_chunks) - { - recvinfo_clear(info); - return 0; - } - - /* TODO: add checking here so we don't read too far */ - for(i = 0; i < info->current_chunk; i++) - { - data = unpack_chunk_header(data, &header); - data += header.size; - } - - /* unpack the header */ - data = unpack_chunk_header(data, &header); - info->current_chunk++; - - if(data+header.size > end) - { - recvinfo_clear(info); - return 0; - } - - /* handle sequence stuff */ - if(info->conn && (header.flags&NET_CHUNKFLAG_VITAL)) - { - if(header.sequence == (info->conn->ack+1)%NET_MAX_SEQUENCE) - { - /* in sequence */ - info->conn->ack = (info->conn->ack+1)%NET_MAX_SEQUENCE; - } - else - { - /* old packet that we already got */ - if(seq_in_backroom(header.sequence, info->conn->ack)) - continue; - - /* out of sequence, request resend */ - if(config.debug) - dbg_msg("conn", "asking for resend %d %d", header.sequence, (info->conn->ack+1)%NET_MAX_SEQUENCE); - conn_want_resend(info->conn); - continue; /* take the next chunk in the packet */ - } - } - - /* fill in the info */ - chunk->client_id = info->client_id; - chunk->address = info->addr; - chunk->flags = 0; - chunk->data_size = header.size; - chunk->data = data; - return 1; - } -} - - -static IOHANDLE datalog_sent = 0; -static IOHANDLE datalog_recv = 0; -static HUFFMAN_STATE huffmanstate; - -#define COMPRESSION 1 - -/* packs the data tight and sends it */ -void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size) -{ - unsigned char buffer[NET_MAX_PACKETSIZE]; - buffer[0] = 0xff; - buffer[1] = 0xff; - buffer[2] = 0xff; - buffer[3] = 0xff; - buffer[4] = 0xff; - buffer[5] = 0xff; - mem_copy(&buffer[6], data, data_size); - net_udp_send(socket, addr, buffer, 6+data_size); -} - -int netcommon_compress(const void *data, int data_size, void *output, int output_size) -{ - return huffman_compress(&huffmanstate, data, data_size, output, output_size); -} - -int netcommon_decompress(const void *data, int data_size, void *output, int output_size) -{ - return huffman_decompress(&huffmanstate, data, data_size, output, output_size); -} - -void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet) -{ - unsigned char buffer[NET_MAX_PACKETSIZE]; - int compressed_size = -1; - int final_size = -1; - - /* log the data */ - if(datalog_sent) - { - int type = 1; - io_write(datalog_sent, &type, sizeof(type)); - io_write(datalog_sent, &packet->data_size, sizeof(packet->data_size)); - io_write(datalog_sent, &packet->chunk_data, packet->data_size); - io_flush(datalog_sent); - } - - /* compress if its enabled */ - if(COMPRESSION) - compressed_size = huffman_compress(&huffmanstate, packet->chunk_data, packet->data_size, &buffer[3], NET_MAX_PACKETSIZE-4); - - /* check if the compression was enabled, successful and good enough */ - if(compressed_size > 0 && compressed_size < packet->data_size) - { - final_size = compressed_size; - packet->flags |= NET_PACKETFLAG_COMPRESSION; - } - else - { - /* use uncompressed data */ - final_size = packet->data_size; - mem_copy(&buffer[3], packet->chunk_data, packet->data_size); - packet->flags &= ~NET_PACKETFLAG_COMPRESSION; - } - - /* set header and send the packet if all things are good */ - if(final_size >= 0) - { - final_size += NET_PACKETHEADERSIZE; - buffer[0] = ((packet->flags<<4)&0xf0)|((packet->ack>>8)&0xf); - buffer[1] = packet->ack&0xff; - buffer[2] = packet->num_chunks; - net_udp_send(socket, addr, buffer, final_size); - - /* log raw socket data */ - if(datalog_sent) - { - int type = 0; - io_write(datalog_sent, &type, sizeof(type)); - io_write(datalog_sent, &final_size, sizeof(final_size)); - io_write(datalog_sent, buffer, final_size); - io_flush(datalog_sent); - } - } -} - -/* TODO: rename this function */ -int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet) -{ - /* check the size */ - if(size < NET_PACKETHEADERSIZE || size > NET_MAX_PACKETSIZE) - { - dbg_msg("", "packet too small, %d", size); - return -1; - } - - /* log the data */ - if(datalog_recv) - { - int type = 0; - io_write(datalog_recv, &type, sizeof(type)); - io_write(datalog_recv, &size, sizeof(size)); - io_write(datalog_recv, buffer, size); - io_flush(datalog_recv); - } - - /* read the packet */ - packet->flags = buffer[0]>>4; - packet->ack = ((buffer[0]&0xf)<<8) | buffer[1]; - packet->num_chunks = buffer[2]; - packet->data_size = size - NET_PACKETHEADERSIZE; - - if(packet->flags&NET_PACKETFLAG_CONNLESS) - { - if(size < 6) - { - dbg_msg("", "connection less packet too small, %d", size); - return -1; - } - - packet->flags = NET_PACKETFLAG_CONNLESS; - packet->ack = 0; - packet->num_chunks = 0; - packet->data_size = size - 6; - mem_copy(packet->chunk_data, &buffer[6], packet->data_size); - } - else - { - if(packet->flags&NET_PACKETFLAG_COMPRESSION) - packet->data_size = huffman_decompress(&huffmanstate, &buffer[3], packet->data_size, packet->chunk_data, sizeof(packet->chunk_data)); - else - mem_copy(packet->chunk_data, &buffer[3], packet->data_size); - } - - /* check for errors */ - if(packet->data_size < 0) - { - if(config.debug) - dbg_msg("network", "error during packet decoding"); - return -1; - } - - /* log the data */ - if(datalog_recv) - { - int type = 1; - io_write(datalog_recv, &type, sizeof(type)); - io_write(datalog_recv, &packet->data_size, sizeof(packet->data_size)); - io_write(datalog_recv, packet->chunk_data, packet->data_size); - io_flush(datalog_recv); - } - - /* return success */ - return 0; -} - - -/* TODO: change the arguments of this function */ -unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence) -{ - data[0] = ((flags&3)<<6)|((size>>4)&0x3f); - data[1] = (size&0xf); - if(flags&NET_CHUNKFLAG_VITAL) - { - data[1] |= (sequence>>2)&0xf0; - data[2] = sequence&0xff; - return data + 3; - } - return data + 2; -} - -unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header) -{ - header->flags = (data[0]>>6)&3; - header->size = ((data[0]&0x3f)<<4) | (data[1]&0xf); - header->sequence = -1; - if(header->flags&NET_CHUNKFLAG_VITAL) - { - header->sequence = ((data[1]&0xf0)<<2) | data[2]; - return data + 3; - } - return data + 2; -} - - -void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size) -{ - NETPACKETCONSTRUCT construct; - construct.flags = NET_PACKETFLAG_CONTROL; - construct.ack = ack; - construct.num_chunks = 0; - construct.data_size = 1+extra_size; - construct.chunk_data[0] = controlmsg; - mem_copy(&construct.chunk_data[1], extra, extra_size); - - /* send the control message */ - send_packet(socket, addr, &construct); -} - -void netcommon_openlog(const char *sentlog, const char *recvlog) -{ - if(sentlog) - { - datalog_sent = engine_openfile(sentlog, IOFLAG_WRITE); - if(datalog_sent) - dbg_msg("network", "logging sent packages to '%s'", sentlog); - else - dbg_msg("network", "failed to open for logging '%s'", sentlog); - } - - if(recvlog) - { - datalog_recv = engine_openfile(recvlog, IOFLAG_WRITE); - if(recvlog) - dbg_msg("network", "logging recv packages to '%s'", recvlog); - else - dbg_msg("network", "failed to open for logging '%s'", recvlog); - } -} - -static const unsigned freq_table[256+1] = { - 1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186, - 283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176, - 872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19, - 16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68, - 59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9, - 20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5, - 15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71, - 148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80, - 167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60, - 136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21, - 32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21, - 12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18, - 9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517}; - -void netcommon_init() -{ - huffman_init(&huffmanstate, freq_table); -} diff --git a/src/engine/e_network.cpp b/src/engine/e_network.cpp new file mode 100644 index 00000000..ac753e50 --- /dev/null +++ b/src/engine/e_network.cpp @@ -0,0 +1,347 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include <base/system.h> + +#include <string.h> /* strlen */ + +#include "e_config.h" +#include "e_engine.h" +#include "e_network.h" +#include "e_huffman.h" + +void CNetRecvUnpacker::Clear() +{ + m_Valid = false; +} + +void CNetRecvUnpacker::Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID) +{ + m_Addr = *pAddr; + m_pConnection = pConnection; + m_ClientID = ClientID; + m_CurrentChunk = 0; + m_Valid = true; +} + +/* TODO: rename this function */ +int CNetRecvUnpacker::FetchChunk(CNetChunk *pChunk) +{ + CNetChunkHeader Header; + unsigned char *pEnd = m_Data.m_aChunkData + m_Data.m_DataSize; + + while(1) + { + unsigned char *pData = m_Data.m_aChunkData; + + /* check for old data to unpack */ + if(!m_Valid || m_CurrentChunk >= m_Data.m_NumChunks) + { + Clear(); + return 0; + } + + /* TODO: add checking here so we don't read too far */ + for(int i = 0; i < m_CurrentChunk; i++) + { + pData = Header.Unpack(pData); + pData += Header.m_Size; + } + + /* unpack the header */ + pData = Header.Unpack(pData); + m_CurrentChunk++; + + if(pData+Header.m_Size > pEnd) + { + Clear(); + return 0; + } + + /* handle sequence stuff */ + if(m_pConnection && (Header.m_Flags&NET_CHUNKFLAG_VITAL)) + { + if(Header.m_Sequence == (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE) + { + /* in sequence */ + m_pConnection->m_Ack = (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE; + } + else + { + /* old packet that we already got */ + if(CNetBase::IsSeqInBackroom(Header.m_Sequence, m_pConnection->m_Ack)) + continue; + + /* out of sequence, request resend */ + if(config.debug) + dbg_msg("conn", "asking for resend %d %d", Header.m_Sequence, (m_pConnection->m_Ack+1)%NET_MAX_SEQUENCE); + m_pConnection->SignalResend(); + continue; /* take the next chunk in the packet */ + } + } + + /* fill in the info */ + pChunk->m_ClientID = m_ClientID; + pChunk->m_Address = m_Addr; + pChunk->m_Flags = 0; + pChunk->m_DataSize = Header.m_Size; + pChunk->m_pData = pData; + return 1; + } +} + +/* packs the data tight and sends it */ +void CNetBase::SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize) +{ + unsigned char aBuffer[NET_MAX_PACKETSIZE]; + aBuffer[0] = 0xff; + aBuffer[1] = 0xff; + aBuffer[2] = 0xff; + aBuffer[3] = 0xff; + aBuffer[4] = 0xff; + aBuffer[5] = 0xff; + mem_copy(&aBuffer[6], pData, DataSize); + net_udp_send(Socket, pAddr, aBuffer, 6+DataSize); +} + +void CNetBase::SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket) +{ + unsigned char aBuffer[NET_MAX_PACKETSIZE]; + int CompressedSize = -1; + int FinalSize = -1; + + /* log the data */ + if(ms_DataLogSent) + { + int type = 1; + io_write(ms_DataLogSent, &type, sizeof(type)); + io_write(ms_DataLogSent, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize)); + io_write(ms_DataLogSent, &pPacket->m_aChunkData, pPacket->m_DataSize); + io_flush(ms_DataLogSent); + } + + /* compress */ + CompressedSize = huffman_compress(&ms_HuffmanState, pPacket->m_aChunkData, pPacket->m_DataSize, &aBuffer[3], NET_MAX_PACKETSIZE-4); + + /* check if the compression was enabled, successful and good enough */ + if(CompressedSize > 0 && CompressedSize < pPacket->m_DataSize) + { + FinalSize = CompressedSize; + pPacket->m_Flags |= NET_PACKETFLAG_COMPRESSION; + } + else + { + /* use uncompressed data */ + FinalSize = pPacket->m_DataSize; + mem_copy(&aBuffer[3], pPacket->m_aChunkData, pPacket->m_DataSize); + pPacket->m_Flags &= ~NET_PACKETFLAG_COMPRESSION; + } + + /* set header and send the packet if all things are good */ + if(FinalSize >= 0) + { + FinalSize += NET_PACKETHEADERSIZE; + aBuffer[0] = ((pPacket->m_Flags<<4)&0xf0)|((pPacket->m_Ack>>8)&0xf); + aBuffer[1] = pPacket->m_Ack&0xff; + aBuffer[2] = pPacket->m_NumChunks; + net_udp_send(Socket, pAddr, aBuffer, FinalSize); + + /* log raw socket data */ + if(ms_DataLogSent) + { + int type = 0; + io_write(ms_DataLogSent, &type, sizeof(type)); + io_write(ms_DataLogSent, &FinalSize, sizeof(FinalSize)); + io_write(ms_DataLogSent, aBuffer, FinalSize); + io_flush(ms_DataLogSent); + } + } +} + +/* TODO: rename this function */ +int CNetBase::UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket) +{ + /* check the size */ + if(Size < NET_PACKETHEADERSIZE || Size > NET_MAX_PACKETSIZE) + { + dbg_msg("", "packet too small, %d", Size); + return -1; + } + + /* log the data */ + if(ms_DataLogRecv) + { + int type = 0; + io_write(ms_DataLogRecv, &type, sizeof(type)); + io_write(ms_DataLogRecv, &Size, sizeof(Size)); + io_write(ms_DataLogRecv, pBuffer, Size); + io_flush(ms_DataLogRecv); + } + + /* read the packet */ + pPacket->m_Flags = pBuffer[0]>>4; + pPacket->m_Ack = ((pBuffer[0]&0xf)<<8) | pBuffer[1]; + pPacket->m_NumChunks = pBuffer[2]; + pPacket->m_DataSize = Size - NET_PACKETHEADERSIZE; + + if(pPacket->m_Flags&NET_PACKETFLAG_CONNLESS) + { + if(Size < 6) + { + dbg_msg("", "connection less packet too small, %d", Size); + return -1; + } + + pPacket->m_Flags = NET_PACKETFLAG_CONNLESS; + pPacket->m_Ack = 0; + pPacket->m_NumChunks = 0; + pPacket->m_DataSize = Size - 6; + mem_copy(pPacket->m_aChunkData, &pBuffer[6], pPacket->m_DataSize); + } + else + { + if(pPacket->m_Flags&NET_PACKETFLAG_COMPRESSION) + pPacket->m_DataSize = huffman_decompress(&ms_HuffmanState, &pBuffer[3], pPacket->m_DataSize, pPacket->m_aChunkData, sizeof(pPacket->m_aChunkData)); + else + mem_copy(pPacket->m_aChunkData, &pBuffer[3], pPacket->m_DataSize); + } + + /* check for errors */ + if(pPacket->m_DataSize < 0) + { + if(config.debug) + dbg_msg("network", "error during packet decoding"); + return -1; + } + + /* log the data */ + if(ms_DataLogRecv) + { + int type = 1; + io_write(ms_DataLogRecv, &type, sizeof(type)); + io_write(ms_DataLogRecv, &pPacket->m_DataSize, sizeof(pPacket->m_DataSize)); + io_write(ms_DataLogRecv, pPacket->m_aChunkData, pPacket->m_DataSize); + io_flush(ms_DataLogRecv); + } + + /* return success */ + return 0; +} + + +void CNetBase::SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize) +{ + CNetPacketConstruct Construct; + Construct.m_Flags = NET_PACKETFLAG_CONTROL; + Construct.m_Ack = Ack; + Construct.m_NumChunks = 0; + Construct.m_DataSize = 1+ExtraSize; + Construct.m_aChunkData[0] = ControlMsg; + mem_copy(&Construct.m_aChunkData[1], pExtra, ExtraSize); + + /* send the control message */ + CNetBase::SendPacket(Socket, pAddr, &Construct); +} + + + +unsigned char *CNetChunkHeader::Pack(unsigned char *pData) +{ + pData[0] = ((m_Flags&3)<<6)|((m_Size>>4)&0x3f); + pData[1] = (m_Size&0xf); + if(m_Flags&NET_CHUNKFLAG_VITAL) + { + pData[1] |= (m_Sequence>>2)&0xf0; + pData[2] = m_Sequence&0xff; + return pData + 3; + } + return pData + 2; +} + +unsigned char *CNetChunkHeader::Unpack(unsigned char *pData) +{ + m_Flags = (pData[0]>>6)&3; + m_Size = ((pData[0]&0x3f)<<4) | (pData[1]&0xf); + m_Sequence = -1; + if(m_Flags&NET_CHUNKFLAG_VITAL) + { + m_Sequence = ((pData[1]&0xf0)<<2) | pData[2]; + return pData + 3; + } + return pData + 2; +} + + +int CNetBase::IsSeqInBackroom(int Seq, int Ack) +{ + int Bottom = (Ack-NET_MAX_SEQUENCE/2); + if(Bottom < 0) + { + if(Seq <= Ack) + return 1; + if(Seq >= (Bottom + NET_MAX_SEQUENCE)) + return 1; + } + else + { + if(Seq <= Ack && Seq >= Bottom) + return 1; + } + + return 0; +} + +IOHANDLE CNetBase::ms_DataLogSent = 0; +IOHANDLE CNetBase::ms_DataLogRecv = 0; +HUFFMAN_STATE CNetBase::ms_HuffmanState; + + +void CNetBase::OpenLog(const char *pSentLog, const char *pRecvLog) +{ + if(pSentLog) + { + ms_DataLogSent = engine_openfile(pSentLog, IOFLAG_WRITE); + if(ms_DataLogSent) + dbg_msg("network", "logging sent packages to '%s'", pSentLog); + else + dbg_msg("network", "failed to open for logging '%s'", pSentLog); + } + + if(pRecvLog) + { + ms_DataLogRecv = engine_openfile(pRecvLog, IOFLAG_WRITE); + if(ms_DataLogRecv) + dbg_msg("network", "logging recv packages to '%s'", pRecvLog); + else + dbg_msg("network", "failed to open for logging '%s'", pRecvLog); + } +} + +int CNetBase::Compress(const void *pData, int DataSize, void *pOutput, int OutputSize) +{ + return huffman_compress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize); +} + +int CNetBase::Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize) +{ + return huffman_decompress(&ms_HuffmanState, pData, DataSize, pOutput, OutputSize); +} + + +static const unsigned gs_aFreqTable[256+1] = { + 1<<30,4545,2657,431,1950,919,444,482,2244,617,838,542,715,1814,304,240,754,212,647,186, + 283,131,146,166,543,164,167,136,179,859,363,113,157,154,204,108,137,180,202,176, + 872,404,168,134,151,111,113,109,120,126,129,100,41,20,16,22,18,18,17,19, + 16,37,13,21,362,166,99,78,95,88,81,70,83,284,91,187,77,68,52,68, + 59,66,61,638,71,157,50,46,69,43,11,24,13,19,10,12,12,20,14,9, + 20,20,10,10,15,15,12,12,7,19,15,14,13,18,35,19,17,14,8,5, + 15,17,9,15,14,18,8,10,2173,134,157,68,188,60,170,60,194,62,175,71, + 148,67,167,78,211,67,156,69,1674,90,174,53,147,89,181,51,174,63,163,80, + 167,94,128,122,223,153,218,77,200,110,190,73,174,69,145,66,277,143,141,60, + 136,53,180,57,142,57,158,61,166,112,152,92,26,22,21,28,20,26,30,21, + 32,27,20,17,23,21,30,22,22,21,27,25,17,27,23,18,39,26,15,21, + 12,18,18,27,20,18,15,19,11,17,33,12,18,15,19,18,16,26,17,18, + 9,10,25,22,22,17,20,16,6,16,15,20,14,18,24,335,1517}; + +void CNetBase::Init() +{ + huffman_init(&ms_HuffmanState, gs_aFreqTable); +} diff --git a/src/engine/e_network.h b/src/engine/e_network.h index 04453cf9..19eca8da 100644 --- a/src/engine/e_network.h +++ b/src/engine/e_network.h @@ -2,38 +2,26 @@ #define ENGINE_NETWORK_H /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include "e_ringbuffer.h" +#include "e_huffman.h" -typedef struct -{ - /* -1 means that it's a stateless packet */ - /* 0 on the client means the server */ - int client_id; - NETADDR address; /* only used when client_id == -1 */ - int flags; - int data_size; - const void *data; -} NETCHUNK; - - -typedef struct -{ - NETADDR addr; - int expires; -} NETBANINFO; +/* -/*typedef struct -{ - int send_bytes; - int recv_bytes; - int send_packets; - int recv_packets; - - int resend_packets; - int resend_bytes; -} NETSTATS;*/ +CURRENT: + packet header: 3 bytes + unsigned char flags_ack; // 4bit flags, 4bit ack + unsigned char ack; // 8 bit ack + unsigned char num_chunks; // 8 bit chunks + + (unsigned char padding[3]) // 24 bit extra incase it's a connection less packet + // this is to make sure that it's compatible with the + // old protocol -typedef struct NETSERVER NETSERVER; -typedef struct NETCLIENT NETCLIENT; + chunk header: 2-3 bytes + unsigned char flags_size; // 2bit flags, 6 bit size + unsigned char size_seq; // 4bit size, 4bit seq + (unsigned char seq;) // 8bit seq, if vital flag is set +*/ enum { @@ -50,97 +38,310 @@ enum NETBANTYPE_DROP=2 }; + +enum +{ + NET_VERSION = 2, + + NET_MAX_CHUNKSIZE = 1024, + NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16, + NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16, + NET_MAX_CHUNKHEADERSIZE = 5, + NET_PACKETHEADERSIZE = 3, + NET_MAX_CLIENTS = 16, + NET_MAX_SEQUENCE = 1<<10, + NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, + + NET_CONNSTATE_OFFLINE=0, + NET_CONNSTATE_CONNECT=1, + NET_CONNSTATE_PENDING=2, + NET_CONNSTATE_ONLINE=3, + NET_CONNSTATE_ERROR=4, + + NET_PACKETFLAG_CONTROL=1, + NET_PACKETFLAG_CONNLESS=2, + NET_PACKETFLAG_RESEND=4, + NET_PACKETFLAG_COMPRESSION=8, + + NET_CHUNKFLAG_VITAL=1, + NET_CHUNKFLAG_RESEND=2, + + NET_CTRLMSG_KEEPALIVE=0, + NET_CTRLMSG_CONNECT=1, + NET_CTRLMSG_CONNECTACCEPT=2, + NET_CTRLMSG_ACCEPT=3, + NET_CTRLMSG_CLOSE=4, + + NET_SERVER_MAXBANS=1024, + + NET_CONN_BUFFERSIZE=1024*16, + + NET_ENUM_TERMINATOR +}; + + typedef int (*NETFUNC_DELCLIENT)(int cid, void *user); typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user); -/* both */ -void netcommon_openlog(const char *sentlog, const char *recvlog); -void netcommon_init(); -int netcommon_compress(const void *data, int data_size, void *output, int output_size); -int netcommon_decompress(const void *data, int data_size, void *output, int output_size); +struct CNetChunk +{ + /* -1 means that it's a stateless packet */ + /* 0 on the client means the server */ + int m_ClientID; + NETADDR m_Address; /* only used when client_id == -1 */ + int m_Flags; + int m_DataSize; + const void *m_pData; +}; -/* server side */ -NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags); -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user); -int netserver_recv(NETSERVER *s, NETCHUNK *chunk); -int netserver_send(NETSERVER *s, NETCHUNK *chunk); -int netserver_close(NETSERVER *s); -int netserver_update(NETSERVER *s); -NETSOCKET netserver_socket(NETSERVER *s); -int netserver_drop(NETSERVER *s, int client_id, const char *reason); -int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr); -int netserver_max_clients(NETSERVER *s); - -int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds); -int netserver_ban_remove(NETSERVER *s, NETADDR addr); -int netserver_ban_num(NETSERVER *s); /* caution, slow */ -int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info); /* caution, slow */ - -/*void netserver_stats(NETSERVER *s, NETSTATS *stats);*/ +class CNetChunkHeader +{ +public: + int m_Flags; + int m_Size; + int m_Sequence; + + unsigned char *Pack(unsigned char *pData); + unsigned char *Unpack(unsigned char *pData); +}; -/* client side */ -NETCLIENT *netclient_open(NETADDR bindaddr, int flags); -int netclient_disconnect(NETCLIENT *c, const char *reason); -int netclient_connect(NETCLIENT *c, NETADDR *addr); -int netclient_recv(NETCLIENT *c, NETCHUNK *chunk); -int netclient_send(NETCLIENT *c, NETCHUNK *chunk); -int netclient_close(NETCLIENT *c); -int netclient_update(NETCLIENT *c); -int netclient_state(NETCLIENT *c); -int netclient_flush(NETCLIENT *c); -int netclient_gotproblems(NETCLIENT *c); -/*void netclient_stats(NETCLIENT *c, NETSTATS *stats);*/ -int netclient_error_string_reset(NETCLIENT *c); -const char *netclient_error_string(NETCLIENT *c); - -#ifdef __cplusplus -class net_server +class CNetChunkResend +{ +public: + int m_Flags; + int m_DataSize; + unsigned char *m_pData; + + int m_Sequence; + int64 m_LastSendTime; + int64 m_FirstSendTime; +}; + +class CNetPacketConstruct { - NETSERVER *ptr; public: - net_server() : ptr(0) {} - ~net_server() { close(); } + int m_Flags; + int m_Ack; + int m_NumChunks; + int m_DataSize; + unsigned char m_aChunkData[NET_MAX_PAYLOAD]; +}; + + +class CNetConnection +{ + // TODO: is this needed because this needs to be aware of + // the ack sequencing number and is also responible for updating + // that. this should be fixed. + friend class CNetRecvUnpacker; +private: + unsigned short m_Sequence; + unsigned short m_Ack; + unsigned m_State; + + int m_Token; + int m_RemoteClosed; + + TStaticRingBuffer<CNetChunkResend, NET_CONN_BUFFERSIZE> m_Buffer; + + int64 m_LastUpdateTime; + int64 m_LastRecvTime; + int64 m_LastSendTime; + + char m_ErrorString[256]; - int open(NETADDR bindaddr, int max, int flags) { ptr = netserver_open(bindaddr, max, flags); return ptr != 0; } - int close() { int r = netserver_close(ptr); ptr = 0; return r; } + CNetPacketConstruct m_Construct; - int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) - { return netserver_set_callbacks(ptr, new_client, del_client, user); } + NETADDR m_PeerAddr; + NETSOCKET m_Socket; + NETSTATS m_Stats; - int recv(NETCHUNK *chunk) { return netserver_recv(ptr, chunk); } - int send(NETCHUNK *chunk) { return netserver_send(ptr, chunk); } - int update() { return netserver_update(ptr); } + // + void Reset(); + void ResetStats(); + void SetError(const char *pString); + void AckChunks(int Ack); - int drop(int client_id, const char *reason) { return netserver_drop(ptr, client_id, reason); } + void QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence); + void SendControl(int ControlMsg, const void *pExtra, int ExtraSize); + void ResendChunk(CNetChunkResend *pResend); + void Resend(); - int max_clients() { return netserver_max_clients(ptr); } - /*void stats(NETSTATS *stats) { netserver_stats(ptr, stats); }*/ +public: + void Init(NETSOCKET Socket); + int Connect(NETADDR *pAddr); + void Disconnect(const char *pReason); + + int Update(); + int Flush(); + + int Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr); + void QueueChunk(int Flags, int DataSize, const void *pData); + + const char *ErrorString(); + void SignalResend(); + int State() const { return m_State; } + NETADDR PeerAddress() const { return m_PeerAddr; } + + void ResetErrorString() { m_ErrorString[0] = 0; } + const char *ErrorString() const { return m_ErrorString; } + + // Needed for GotProblems in NetClient + int64 LastRecvTime() const { return m_LastRecvTime; } + + int AckSequence() const { return m_Ack; } +}; + +struct CNetRecvUnpacker +{ +public: + bool m_Valid; + + NETADDR m_Addr; + CNetConnection *m_pConnection; + int m_CurrentChunk; + int m_ClientID; + CNetPacketConstruct m_Data; + unsigned char m_aBuffer[NET_MAX_PACKETSIZE]; + + CNetRecvUnpacker() { Clear(); } + void Clear(); + void Start(const NETADDR *pAddr, CNetConnection *pConnection, int ClientID); + int FetchChunk(CNetChunk *pChunk); +}; + +/* server side */ +class CNetServer +{ +public: + struct CBanInfo + { + NETADDR m_Addr; + int m_Expires; + }; + +private: + class CSlot + { + public: + CNetConnection m_Connection; + }; + + class CBan + { + public: + CBanInfo m_Info; + + /* hash list */ + CBan *m_pHashNext; + CBan *m_pHashPrev; + + /* used or free list */ + CBan *m_pNext; + CBan *m_pPrev; + }; + + + NETSOCKET m_Socket; + CSlot m_aSlots[NET_MAX_CLIENTS]; + int m_MaxClients; + + CBan *m_aBans[256]; + CBan m_BanPool[NET_SERVER_MAXBANS]; + CBan *m_BanPool_FirstFree; + CBan *m_BanPool_FirstUsed; + + NETFUNC_NEWCLIENT m_pfnNewClient; + NETFUNC_DELCLIENT m_pfnDelClient; + void *m_UserPtr; + + CNetRecvUnpacker m_RecvUnpacker; + + void BanRemoveByObject(CBan *ban); + +public: + int SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser); + + // + bool Open(NETADDR bindaddr, int MaxClients, int Flags); + int Close(); + + // + int Recv(CNetChunk *pChunk); + int Send(CNetChunk *pChunk); + int Update(); + + // + int Drop(int ClientID, const char *Reason); + + // banning + int BanAdd(NETADDR Addr, int Seconds); + int BanRemove(NETADDR Addr); + int BanNum(); /* caution, slow */ + int BanGet(int Index, CBanInfo *pInfo); /* caution, slow */ + + // status requests + NETADDR ClientAddr(int ClientID) const { return m_aSlots[ClientID].m_Connection.PeerAddress(); } + NETSOCKET Socket() const { return m_Socket; } + int MaxClients() const { return m_MaxClients; } }; -class net_client + +/* client side */ +class CNetClient { - NETCLIENT *ptr; + NETADDR m_ServerAddr; + CNetConnection m_Connection; + CNetRecvUnpacker m_RecvUnpacker; + NETSOCKET m_Socket; public: - net_client() : ptr(0) {} - ~net_client() { close(); } + // openness + bool Open(NETADDR BindAddr, int Flags); + int Close(); - int open(NETADDR bindaddr, int flags) { ptr = netclient_open(bindaddr, flags); return ptr != 0; } - int close() { int r = netclient_close(ptr); ptr = 0; return r; } + // connection state + int Disconnect(const char *Reason); + int Connect(NETADDR *Addr); - int connect(NETADDR *addr) { return netclient_connect(ptr, addr); } - int disconnect(const char *reason) { return netclient_disconnect(ptr, reason); } + // communication + int Recv(CNetChunk *Chunk); + int Send(CNetChunk *Chunk); - int recv(NETCHUNK *chunk) { return netclient_recv(ptr, chunk); } - int send(NETCHUNK *chunk) { return netclient_send(ptr, chunk); } - int update() { return netclient_update(ptr); } + // pumping + int Update(); + int Flush(); + + int ResetErrorString(); - const char *error_string() { return netclient_error_string(ptr); } + // error and state + int State(); + int GotProblems(); + const char *ErrorString(); +}; + + + +// TODO: both, fix these. This feels like a junk class for stuff that doesn't fit anywere +class CNetBase +{ + static IOHANDLE ms_DataLogSent; + static IOHANDLE ms_DataLogRecv; + static HUFFMAN_STATE ms_HuffmanState; +public: + static void OpenLog(const char *pSentlog, const char *pRecvlog); + static void Init(); + static int Compress(const void *pData, int DataSize, void *pOutput, int OutputSize); + static int Decompress(const void *pData, int DataSize, void *pOutput, int OutputSize); - int state() { return netclient_state(ptr); } - /*void stats(NETSTATS *stats) { netclient_stats(ptr, stats); }*/ + static void SendControlMsg(NETSOCKET Socket, NETADDR *pAddr, int Ack, int ControlMsg, const void *pExtra, int ExtraSize); + static void SendPacketConnless(NETSOCKET Socket, NETADDR *pAddr, const void *pData, int DataSize); + static void SendPacket(NETSOCKET Socket, NETADDR *pAddr, CNetPacketConstruct *pPacket); + static int UnpackPacket(unsigned char *pBuffer, int Size, CNetPacketConstruct *pPacket); + + /* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */ + static int IsSeqInBackroom(int Seq, int Ack); }; -#endif #endif diff --git a/src/engine/e_network_client.c b/src/engine/e_network_client.c deleted file mode 100644 index 75f7c538..00000000 --- a/src/engine/e_network_client.c +++ /dev/null @@ -1,154 +0,0 @@ -#include <base/system.h> -#include "e_network.h" -#include "e_network_internal.h" - -struct NETCLIENT -{ - NETADDR server_addr; - NETSOCKET socket; - - NETRECVINFO recv; - NETCONNECTION conn; -}; - -NETCLIENT *netclient_open(NETADDR bindaddr, int flags) -{ - NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); - mem_zero(client, sizeof(NETCLIENT)); - client->socket = net_udp_create(bindaddr); - conn_init(&client->conn, client->socket); - return client; -} - -int netclient_close(NETCLIENT *c) -{ - /* TODO: implement me */ - return 0; -} - - -int netclient_disconnect(NETCLIENT *c, const char *reason) -{ - dbg_msg("netclient", "disconnected. reason=\"%s\"", reason); - conn_disconnect(&c->conn, reason); - return 0; -} - -int netclient_update(NETCLIENT *c) -{ - conn_update(&c->conn); - if(c->conn.state == NET_CONNSTATE_ERROR) - netclient_disconnect(c, conn_error(&c->conn)); - return 0; -} - -int netclient_connect(NETCLIENT *c, NETADDR *addr) -{ - conn_connect(&c->conn, addr); - return 0; -} - -int netclient_error_string_reset(NETCLIENT *c) -{ - mem_zero(c->conn.error_string, sizeof(c->conn.error_string)); - return 0; -} - -int netclient_recv(NETCLIENT *c, NETCHUNK *chunk) -{ - while(1) - { - NETADDR addr; - int bytes; - - /* check for a chunk */ - if(recvinfo_fetch_chunk(&c->recv, chunk)) - return 1; - - /* TODO: empty the recvinfo */ - bytes = net_udp_recv(c->socket, &addr, c->recv.buffer, NET_MAX_PACKETSIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - if(unpack_packet(c->recv.buffer, bytes, &c->recv.data) == 0) - { - if(c->recv.data.flags&NET_PACKETFLAG_CONNLESS) - { - chunk->flags = NETSENDFLAG_CONNLESS; - chunk->client_id = -1; - chunk->address = addr; - chunk->data_size = c->recv.data.data_size; - chunk->data = c->recv.data.chunk_data; - return 1; - } - else - { - if(conn_feed(&c->conn, &c->recv.data, &addr)) - recvinfo_start(&c->recv, &addr, &c->conn, 0); - } - } - } - return 0; -} - -int netclient_send(NETCLIENT *c, NETCHUNK *chunk) -{ - if(chunk->data_size >= NET_MAX_PAYLOAD) - { - dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", chunk->data_size); - return -1; - } - - if(chunk->flags&NETSENDFLAG_CONNLESS) - { - /* send connectionless packet */ - send_packet_connless(c->socket, &chunk->address, chunk->data, chunk->data_size); - } - else - { - int f = 0; - dbg_assert(chunk->client_id == 0, "errornous client id"); - - if(chunk->flags&NETSENDFLAG_VITAL) - f = NET_CHUNKFLAG_VITAL; - - conn_queue_chunk(&c->conn, f, chunk->data_size, chunk->data); - - if(chunk->flags&NETSENDFLAG_FLUSH) - conn_flush(&c->conn); - } - return 0; -} - -int netclient_state(NETCLIENT *c) -{ - if(c->conn.state == NET_CONNSTATE_ONLINE) - return NETSTATE_ONLINE; - if(c->conn.state == NET_CONNSTATE_OFFLINE) - return NETSTATE_OFFLINE; - return NETSTATE_CONNECTING; -} - -int netclient_flush(NETCLIENT *c) -{ - return conn_flush(&c->conn); -} - -int netclient_gotproblems(NETCLIENT *c) -{ - if(time_get() - c->conn.last_recv_time > time_freq()) - return 1; - return 0; -} - -void netclient_stats(NETCLIENT *c, NETSTATS *stats) -{ - *stats = c->conn.stats; -} - -const char *netclient_error_string(NETCLIENT *c) -{ - return conn_error(&c->conn); -} diff --git a/src/engine/e_network_client.cpp b/src/engine/e_network_client.cpp new file mode 100644 index 00000000..ce243b32 --- /dev/null +++ b/src/engine/e_network_client.cpp @@ -0,0 +1,139 @@ +#include <base/system.h> +#include "e_network.h" + +bool CNetClient::Open(NETADDR BindAddr, int Flags) +{ + // clean it + mem_zero(this, sizeof(*this)); + + // open socket + m_Socket = net_udp_create(BindAddr); + m_Connection.Init(m_Socket); + return true; +} + +int CNetClient::Close() +{ + /* TODO: implement me */ + return 0; +} + + +int CNetClient::Disconnect(const char *pReason) +{ + dbg_msg("netclient", "disconnected. reason=\"%s\"", pReason); + m_Connection.Disconnect(pReason); + return 0; +} + +int CNetClient::Update() +{ + m_Connection.Update(); + if(m_Connection.State() == NET_CONNSTATE_ERROR) + Disconnect(m_Connection.ErrorString()); + return 0; +} + +int CNetClient::Connect(NETADDR *pAddr) +{ + m_Connection.Connect(pAddr); + return 0; +} + +int CNetClient::ResetErrorString() +{ + m_Connection.ResetErrorString(); + return 0; +} + +int CNetClient::Recv(CNetChunk *pChunk) +{ + while(1) + { + /* check for a chunk */ + if(m_RecvUnpacker.FetchChunk(pChunk)) + return 1; + + /* TODO: empty the recvinfo */ + NETADDR Addr; + int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); + + /* no more packets for now */ + if(Bytes <= 0) + break; + + if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + { + if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) + { + pChunk->m_Flags = NETSENDFLAG_CONNLESS; + pChunk->m_ClientID = -1; + pChunk->m_Address = Addr; + pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; + pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; + return 1; + } + else + { + if(m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) + m_RecvUnpacker.Start(&Addr, &m_Connection, 0); + } + } + } + return 0; +} + +int CNetClient::Send(CNetChunk *pChunk) +{ + if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) + { + dbg_msg("netclient", "chunk payload too big. %d. dropping chunk", pChunk->m_DataSize); + return -1; + } + + if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) + { + /* send connectionless packet */ + CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); + } + else + { + int Flags = 0; + dbg_assert(pChunk->m_ClientID == 0, "errornous client id"); + + if(pChunk->m_Flags&NETSENDFLAG_VITAL) + Flags = NET_CHUNKFLAG_VITAL; + + m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData); + + if(pChunk->m_Flags&NETSENDFLAG_FLUSH) + m_Connection.Flush(); + } + return 0; +} + +int CNetClient::State() +{ + if(m_Connection.State() == NET_CONNSTATE_ONLINE) + return NETSTATE_ONLINE; + if(m_Connection.State() == NET_CONNSTATE_OFFLINE) + return NETSTATE_OFFLINE; + return NETSTATE_CONNECTING; +} + +int CNetClient::Flush() +{ + return m_Connection.Flush(); +} + +int CNetClient::GotProblems() +{ + if(time_get() - m_Connection.LastRecvTime() > time_freq()) + return 1; + return 0; +} + +const char *CNetClient::ErrorString() +{ + return m_Connection.ErrorString(); +} diff --git a/src/engine/e_network_conn.c b/src/engine/e_network_conn.c deleted file mode 100644 index 583dbe65..00000000 --- a/src/engine/e_network_conn.c +++ /dev/null @@ -1,366 +0,0 @@ -#include <base/system.h> -#include <string.h> -#include "e_config.h" -#include "e_network_internal.h" - -static void conn_reset_stats(NETCONNECTION *conn) -{ - mem_zero(&conn->stats, sizeof(conn->stats)); -} - -static void conn_reset(NETCONNECTION *conn) -{ - conn->seq = 0; - conn->ack = 0; - conn->remote_closed = 0; - - conn->state = NET_CONNSTATE_OFFLINE; - conn->last_send_time = 0; - conn->last_recv_time = 0; - conn->last_update_time = 0; - conn->token = -1; - mem_zero(&conn->peeraddr, sizeof(conn->peeraddr)); - - conn->buffer = ringbuf_init(conn->buffer_memory, sizeof(conn->buffer_memory), 0); - - mem_zero(&conn->construct, sizeof(conn->construct)); -} - - -const char *conn_error(NETCONNECTION *conn) -{ - return conn->error_string; -} - -static void conn_set_error(NETCONNECTION *conn, const char *str) -{ - str_copy(conn->error_string, str, sizeof(conn->error_string)); -} - -void conn_init(NETCONNECTION *conn, NETSOCKET socket) -{ - conn_reset(conn); - conn_reset_stats(conn); - conn->socket = socket; - mem_zero(conn->error_string, sizeof(conn->error_string)); -} - - -static void conn_ack(NETCONNECTION *conn, int ack) -{ - - while(1) - { - NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer); - if(!resend) - break; - - if(seq_in_backroom(resend->sequence, ack)) - ringbuf_popfirst(conn->buffer); - else - break; - } -} - -void conn_want_resend(NETCONNECTION *conn) -{ - conn->construct.flags |= NET_PACKETFLAG_RESEND; -} - -int conn_flush(NETCONNECTION *conn) -{ - int num_chunks = conn->construct.num_chunks; - if(!num_chunks && !conn->construct.flags) - return 0; - - /* send of the packets */ - conn->construct.ack = conn->ack; - send_packet(conn->socket, &conn->peeraddr, &conn->construct); - - /* update send times */ - conn->last_send_time = time_get(); - - /* clear construct so we can start building a new package */ - mem_zero(&conn->construct, sizeof(conn->construct)); - return num_chunks; -} - -static void conn_queue_chunk_ex(NETCONNECTION *conn, int flags, int data_size, const void *data, int sequence) -{ - unsigned char *chunk_data; - - /* check if we have space for it, if not, flush the connection */ - if(conn->construct.data_size + data_size + NET_MAX_CHUNKHEADERSIZE > sizeof(conn->construct.chunk_data)) - conn_flush(conn); - - /* pack all the data */ - chunk_data = &conn->construct.chunk_data[conn->construct.data_size]; - chunk_data = pack_chunk_header(chunk_data, flags, data_size, sequence); - mem_copy(chunk_data, data, data_size); - chunk_data += data_size; - - /* */ - conn->construct.num_chunks++; - conn->construct.data_size = (int)(chunk_data-conn->construct.chunk_data); - - /* set packet flags aswell */ - - if(flags&NET_CHUNKFLAG_VITAL && !(flags&NET_CHUNKFLAG_RESEND)) - { - /* save packet if we need to resend */ - NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_allocate(conn->buffer, sizeof(NETCHUNKDATA)+data_size); - if(resend) - { - resend->sequence = sequence; - resend->flags = flags; - resend->data_size = data_size; - resend->data = (unsigned char *)(resend+1); - resend->first_send_time = time_get(); - resend->last_send_time = resend->first_send_time; - mem_copy(resend->data, data, data_size); - } - else - { - /* out of buffer */ - conn_disconnect(conn, "too weak connection (out of buffer)"); - } - } -} - -void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data) -{ - if(flags&NET_CHUNKFLAG_VITAL) - conn->seq = (conn->seq+1)%NET_MAX_SEQUENCE; - conn_queue_chunk_ex(conn, flags, data_size, data, conn->seq); -} - - -static void conn_send_control(NETCONNECTION *conn, int controlmsg, const void *extra, int extra_size) -{ - /* send the control message */ - conn->last_send_time = time_get(); - send_controlmsg(conn->socket, &conn->peeraddr, conn->ack, controlmsg, extra, extra_size); -} - -static void conn_resend_chunk(NETCONNECTION *conn, NETCHUNKDATA *resend) -{ - conn_queue_chunk_ex(conn, resend->flags|NET_CHUNKFLAG_RESEND, resend->data_size, resend->data, resend->sequence); - resend->last_send_time = time_get(); -} - -static void conn_resend(NETCONNECTION *conn) -{ - int resend_count = 0; - int first = 0, last = 0; - void *item = ringbuf_first(conn->buffer); - - while(item) - { - NETCHUNKDATA *resend = item; - - if(resend_count == 0) - first = resend->sequence; - last = resend->sequence; - - conn_resend_chunk(conn, resend); - item = ringbuf_next(conn->buffer, item); - resend_count++; - } - - if(config.debug) - dbg_msg("conn", "resent %d packets (%d to %d)", resend_count, first, last); -} - -int conn_connect(NETCONNECTION *conn, NETADDR *addr) -{ - if(conn->state != NET_CONNSTATE_OFFLINE) - return -1; - - /* init connection */ - conn_reset(conn); - conn->peeraddr = *addr; - mem_zero(conn->error_string, sizeof(conn->error_string)); - conn->state = NET_CONNSTATE_CONNECT; - conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); - return 0; -} - -void conn_disconnect(NETCONNECTION *conn, const char *reason) -{ - if(conn->state == NET_CONNSTATE_OFFLINE) - return; - - if(conn->remote_closed == 0) - { - if(reason) - conn_send_control(conn, NET_CTRLMSG_CLOSE, reason, strlen(reason)+1); - else - conn_send_control(conn, NET_CTRLMSG_CLOSE, 0, 0); - - conn->error_string[0] = 0; - if(reason) - str_copy(conn->error_string, reason, sizeof(conn->error_string)); - } - - conn_reset(conn); -} - -int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr) -{ - int64 now = time_get(); - conn->last_recv_time = now; - - /* check if resend is requested */ - if(packet->flags&NET_PACKETFLAG_RESEND) - conn_resend(conn); - - /* */ - if(packet->flags&NET_PACKETFLAG_CONTROL) - { - int ctrlmsg = packet->chunk_data[0]; - - if(ctrlmsg == NET_CTRLMSG_CLOSE) - { - conn->state = NET_CONNSTATE_ERROR; - conn->remote_closed = 1; - - if(packet->data_size) - { - /* make sure to sanitize the error string form the other party*/ - char str[128]; - if(packet->data_size < 128) - str_copy(str, (char *)packet->chunk_data, packet->data_size); - else - str_copy(str, (char *)packet->chunk_data, 128); - str_sanitize_strong(str); - - /* set the error string */ - conn_set_error(conn, str); - } - else - conn_set_error(conn, "no reason given"); - - if(config.debug) - dbg_msg("conn", "closed reason='%s'", conn_error(conn)); - return 0; - } - else - { - if(conn->state == NET_CONNSTATE_OFFLINE) - { - if(ctrlmsg == NET_CTRLMSG_CONNECT) - { - /* send response and init connection */ - conn_reset(conn); - conn->state = NET_CONNSTATE_PENDING; - conn->peeraddr = *addr; - conn->last_send_time = now; - conn->last_recv_time = now; - conn->last_update_time = now; - conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); - if(config.debug) - dbg_msg("connection", "got connection, sending connect+accept"); - } - } - else if(conn->state == NET_CONNSTATE_CONNECT) - { - /* connection made */ - if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) - { - conn_send_control(conn, NET_CTRLMSG_ACCEPT, 0, 0); - conn->state = NET_CONNSTATE_ONLINE; - if(config.debug) - dbg_msg("connection", "got connect+accept, sending accept. connection online"); - } - } - else if(conn->state == NET_CONNSTATE_ONLINE) - { - /* connection made */ - /* - if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) - { - - }*/ - } - } - } - else - { - if(conn->state == NET_CONNSTATE_PENDING) - { - conn->state = NET_CONNSTATE_ONLINE; - if(config.debug) - dbg_msg("connection", "connecting online"); - } - } - - if(conn->state == NET_CONNSTATE_ONLINE) - { - - conn_ack(conn, packet->ack); - } - - return 1; -} - -int conn_update(NETCONNECTION *conn) -{ - int64 now = time_get(); - - if(conn->state == NET_CONNSTATE_OFFLINE || conn->state == NET_CONNSTATE_ERROR) - return 0; - - /* check for timeout */ - if(conn->state != NET_CONNSTATE_OFFLINE && - conn->state != NET_CONNSTATE_CONNECT && - (now-conn->last_recv_time) > time_freq()*10) - { - conn->state = NET_CONNSTATE_ERROR; - conn_set_error(conn, "timeout"); - } - - /* fix resends */ - if(ringbuf_first(conn->buffer)) - { - NETCHUNKDATA *resend = (NETCHUNKDATA *)ringbuf_first(conn->buffer); - - /* check if we have some really old stuff laying around and abort if not acked */ - if(now-resend->first_send_time > time_freq()*10) - { - conn->state = NET_CONNSTATE_ERROR; - conn_set_error(conn, "too weak connection (not acked for 10 seconds)"); - } - else - { - /* resend packet if we havn't got it acked in 1 second */ - if(now-resend->last_send_time > time_freq()) - conn_resend_chunk(conn, resend); - } - } - - /* send keep alives if nothing has happend for 250ms */ - if(conn->state == NET_CONNSTATE_ONLINE) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* flush connection after 500ms if needed */ - { - int num_flushed_chunks = conn_flush(conn); - if(num_flushed_chunks && config.debug) - dbg_msg("connection", "flushed connection due to timeout. %d chunks.", num_flushed_chunks); - } - - if(time_get()-conn->last_send_time > time_freq()) - conn_send_control(conn, NET_CTRLMSG_KEEPALIVE, 0, 0); - } - else if(conn->state == NET_CONNSTATE_CONNECT) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect every 500ms */ - conn_send_control(conn, NET_CTRLMSG_CONNECT, 0, 0); - } - else if(conn->state == NET_CONNSTATE_PENDING) - { - if(time_get()-conn->last_send_time > time_freq()/2) /* send a new connect/accept every 500ms */ - conn_send_control(conn, NET_CTRLMSG_CONNECTACCEPT, 0, 0); - } - - return 0; -} diff --git a/src/engine/e_network_conn.cpp b/src/engine/e_network_conn.cpp new file mode 100644 index 00000000..a54c9ce3 --- /dev/null +++ b/src/engine/e_network_conn.cpp @@ -0,0 +1,349 @@ +#include <base/system.h> +#include <string.h> +#include "e_config.h" +#include "e_network.h" + +void CNetConnection::ResetStats() +{ + mem_zero(&m_Stats, sizeof(m_Stats)); +} + +void CNetConnection::Reset() +{ + m_Sequence = 0; + m_Ack = 0; + m_RemoteClosed = 0; + + m_State = NET_CONNSTATE_OFFLINE; + m_LastSendTime = 0; + m_LastRecvTime = 0; + m_LastUpdateTime = 0; + m_Token = -1; + mem_zero(&m_PeerAddr, sizeof(m_PeerAddr)); + + m_Buffer.Init(); + + mem_zero(&m_Construct, sizeof(m_Construct)); +} + +const char *CNetConnection::ErrorString() +{ + return m_ErrorString; +} + +void CNetConnection::SetError(const char *pString) +{ + str_copy(m_ErrorString, pString, sizeof(m_ErrorString)); +} + +void CNetConnection::Init(NETSOCKET Socket) +{ + Reset(); + ResetStats(); + + m_Socket = Socket; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); +} + +void CNetConnection::AckChunks(int Ack) +{ + while(1) + { + CNetChunkResend *pResend = m_Buffer.First(); + if(!pResend) + break; + + if(CNetBase::IsSeqInBackroom(pResend->m_Sequence, Ack)) + m_Buffer.PopFirst(); + else + break; + } +} + +void CNetConnection::SignalResend() +{ + m_Construct.m_Flags |= NET_PACKETFLAG_RESEND; +} + +int CNetConnection::Flush() +{ + int NumChunks = m_Construct.m_NumChunks; + if(!NumChunks && !m_Construct.m_Flags) + return 0; + + /* send of the packets */ + m_Construct.m_Ack = m_Ack; + CNetBase::SendPacket(m_Socket, &m_PeerAddr, &m_Construct); + + /* update send times */ + m_LastSendTime = time_get(); + + /* clear construct so we can start building a new package */ + mem_zero(&m_Construct, sizeof(m_Construct)); + return NumChunks; +} + +void CNetConnection::QueueChunkEx(int Flags, int DataSize, const void *pData, int Sequence) +{ + unsigned char *pChunkData; + + /* check if we have space for it, if not, flush the connection */ + if(m_Construct.m_DataSize + DataSize + NET_MAX_CHUNKHEADERSIZE > (int)sizeof(m_Construct.m_aChunkData)) + Flush(); + + /* pack all the data */ + CNetChunkHeader Header; + Header.m_Flags = Flags; + Header.m_Size = DataSize; + Header.m_Sequence = Sequence; + pChunkData = &m_Construct.m_aChunkData[m_Construct.m_DataSize]; + pChunkData = Header.Pack(pChunkData); + mem_copy(pChunkData, pData, DataSize); + pChunkData += DataSize; + + /* */ + m_Construct.m_NumChunks++; + m_Construct.m_DataSize = (int)(pChunkData-m_Construct.m_aChunkData); + + /* set packet flags aswell */ + + if(Flags&NET_CHUNKFLAG_VITAL && !(Flags&NET_CHUNKFLAG_RESEND)) + { + /* save packet if we need to resend */ + CNetChunkResend *pResend = m_Buffer.Allocate(sizeof(CNetChunkResend)+DataSize); + if(pResend) + { + pResend->m_Sequence = Sequence; + pResend->m_Flags = Flags; + pResend->m_DataSize = DataSize; + pResend->m_pData = (unsigned char *)(pResend+1); + pResend->m_FirstSendTime = time_get(); + pResend->m_LastSendTime = pResend->m_FirstSendTime; + mem_copy(pResend->m_pData, pData, DataSize); + } + else + { + /* out of buffer */ + Disconnect("too weak connection (out of buffer)"); + } + } +} + +void CNetConnection::QueueChunk(int Flags, int DataSize, const void *pData) +{ + if(Flags&NET_CHUNKFLAG_VITAL) + m_Sequence = (m_Sequence+1)%NET_MAX_SEQUENCE; + QueueChunkEx(Flags, DataSize, pData, m_Sequence); +} + +void CNetConnection::SendControl(int ControlMsg, const void *pExtra, int ExtraSize) +{ + /* send the control message */ + m_LastSendTime = time_get(); + CNetBase::SendControlMsg(m_Socket, &m_PeerAddr, m_Ack, ControlMsg, pExtra, ExtraSize); +} + +void CNetConnection::ResendChunk(CNetChunkResend *pResend) +{ + QueueChunkEx(pResend->m_Flags|NET_CHUNKFLAG_RESEND, pResend->m_DataSize, pResend->m_pData, pResend->m_Sequence); + pResend->m_LastSendTime = time_get(); +} + +void CNetConnection::Resend() +{ + for(CNetChunkResend *pResend = m_Buffer.First(); pResend; m_Buffer.Next(pResend)) + ResendChunk(pResend); +} + +int CNetConnection::Connect(NETADDR *pAddr) +{ + if(State() != NET_CONNSTATE_OFFLINE) + return -1; + + /* init connection */ + Reset(); + m_PeerAddr = *pAddr; + mem_zero(m_ErrorString, sizeof(m_ErrorString)); + m_State = NET_CONNSTATE_CONNECT; + SendControl(NET_CTRLMSG_CONNECT, 0, 0); + return 0; +} + +void CNetConnection::Disconnect(const char *pReason) +{ + if(State() == NET_CONNSTATE_OFFLINE) + return; + + if(m_RemoteClosed == 0) + { + if(pReason) + SendControl(NET_CTRLMSG_CLOSE, pReason, strlen(pReason)+1); + else + SendControl(NET_CTRLMSG_CLOSE, 0, 0); + + m_ErrorString[0] = 0; + if(pReason) + str_copy(m_ErrorString, pReason, sizeof(m_ErrorString)); + } + + Reset(); +} + +int CNetConnection::Feed(CNetPacketConstruct *pPacket, NETADDR *pAddr) +{ + int64 now = time_get(); + m_LastRecvTime = now; + + /* check if resend is requested */ + if(pPacket->m_Flags&NET_PACKETFLAG_RESEND) + Resend(); + + /* */ + if(pPacket->m_Flags&NET_PACKETFLAG_CONTROL) + { + int CtrlMsg = pPacket->m_aChunkData[0]; + + if(CtrlMsg == NET_CTRLMSG_CLOSE) + { + m_State = NET_CONNSTATE_ERROR; + m_RemoteClosed = 1; + + if(pPacket->m_DataSize) + { + /* make sure to sanitize the error string form the other party*/ + char Str[128]; + if(pPacket->m_DataSize < 128) + str_copy(Str, (char *)pPacket->m_aChunkData, pPacket->m_DataSize); + else + str_copy(Str, (char *)pPacket->m_aChunkData, sizeof(Str)); + str_sanitize_strong(Str); + + /* set the error string */ + SetError(Str); + } + else + SetError("no reason given"); + + if(config.debug) + dbg_msg("conn", "closed reason='%s'", ErrorString()); + return 0; + } + else + { + if(State() == NET_CONNSTATE_OFFLINE) + { + if(CtrlMsg == NET_CTRLMSG_CONNECT) + { + /* send response and init connection */ + Reset(); + m_State = NET_CONNSTATE_PENDING; + m_PeerAddr = *pAddr; + m_LastSendTime = now; + m_LastRecvTime = now; + m_LastUpdateTime = now; + SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + if(config.debug) + dbg_msg("connection", "got connection, sending connect+accept"); + } + } + else if(State() == NET_CONNSTATE_CONNECT) + { + /* connection made */ + if(CtrlMsg == NET_CTRLMSG_CONNECTACCEPT) + { + SendControl(NET_CTRLMSG_ACCEPT, 0, 0); + m_State = NET_CONNSTATE_ONLINE; + if(config.debug) + dbg_msg("connection", "got connect+accept, sending accept. connection online"); + } + } + else if(State() == NET_CONNSTATE_ONLINE) + { + /* connection made */ + /* + if(ctrlmsg == NET_CTRLMSG_CONNECTACCEPT) + { + + }*/ + } + } + } + else + { + if(State() == NET_CONNSTATE_PENDING) + { + m_State = NET_CONNSTATE_ONLINE; + if(config.debug) + dbg_msg("connection", "connecting online"); + } + } + + if(State() == NET_CONNSTATE_ONLINE) + { + AckChunks(pPacket->m_Ack); + } + + return 1; +} + +int CNetConnection::Update() +{ + int64 now = time_get(); + + if(State() == NET_CONNSTATE_OFFLINE || State() == NET_CONNSTATE_ERROR) + return 0; + + /* check for timeout */ + if(State() != NET_CONNSTATE_OFFLINE && + State() != NET_CONNSTATE_CONNECT && + (now-m_LastRecvTime) > time_freq()*10) + { + m_State = NET_CONNSTATE_ERROR; + SetError("timeout"); + } + + /* fix resends */ + if(m_Buffer.First()) + { + CNetChunkResend *pResend = m_Buffer.First(); + + /* check if we have some really old stuff laying around and abort if not acked */ + if(now-pResend->m_FirstSendTime > time_freq()*10) + { + m_State = NET_CONNSTATE_ERROR; + SetError("too weak connection (not acked for 10 seconds)"); + } + else + { + /* resend packet if we havn't got it acked in 1 second */ + if(now-pResend->m_LastSendTime > time_freq()) + ResendChunk(pResend); + } + } + + /* send keep alives if nothing has happend for 250ms */ + if(State() == NET_CONNSTATE_ONLINE) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* flush connection after 500ms if needed */ + { + int NumFlushedChunks = Flush(); + if(NumFlushedChunks && config.debug) + dbg_msg("connection", "flushed connection due to timeout. %d chunks.", NumFlushedChunks); + } + + if(time_get()-m_LastSendTime > time_freq()) + SendControl(NET_CTRLMSG_KEEPALIVE, 0, 0); + } + else if(State() == NET_CONNSTATE_CONNECT) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect every 500ms */ + SendControl(NET_CTRLMSG_CONNECT, 0, 0); + } + else if(State() == NET_CONNSTATE_PENDING) + { + if(time_get()-m_LastSendTime > time_freq()/2) /* send a new connect/accept every 500ms */ + SendControl(NET_CTRLMSG_CONNECTACCEPT, 0, 0); + } + + return 0; +} diff --git a/src/engine/e_network_internal.h b/src/engine/e_network_internal.h deleted file mode 100644 index f1a25b31..00000000 --- a/src/engine/e_network_internal.h +++ /dev/null @@ -1,156 +0,0 @@ -#include <base/system.h> -#include "e_network.h" -#include "e_ringbuffer.h" - -/* - -CURRENT: - packet header: 3 bytes - unsigned char flags_ack; // 4bit flags, 4bit ack - unsigned char ack; // 8 bit ack - unsigned char num_chunks; // 8 bit chunks - - (unsigned char padding[3]) // 24 bit extra incase it's a connection less packet - // this is to make sure that it's compatible with the - // old protocol - - chunk header: 2-3 bytes - unsigned char flags_size; // 2bit flags, 6 bit size - unsigned char size_seq; // 4bit size, 4bit seq - (unsigned char seq;) // 8bit seq, if vital flag is set - - -*/ - -enum -{ - NET_VERSION = 2, - - NET_MAX_CHUNKSIZE = 1024, - NET_MAX_PAYLOAD = NET_MAX_CHUNKSIZE+16, - NET_MAX_PACKETSIZE = NET_MAX_PAYLOAD+16, - NET_MAX_CHUNKHEADERSIZE = 5, - NET_PACKETHEADERSIZE = 3, - NET_MAX_CLIENTS = 16, - NET_MAX_SEQUENCE = 1<<10, - NET_SEQUENCE_MASK = NET_MAX_SEQUENCE-1, - - NET_CONNSTATE_OFFLINE=0, - NET_CONNSTATE_CONNECT=1, - NET_CONNSTATE_PENDING=2, - NET_CONNSTATE_ONLINE=3, - NET_CONNSTATE_ERROR=4, - - NET_PACKETFLAG_CONTROL=1, - NET_PACKETFLAG_CONNLESS=2, - NET_PACKETFLAG_RESEND=4, - NET_PACKETFLAG_COMPRESSION=8, - - NET_CHUNKFLAG_VITAL=1, - NET_CHUNKFLAG_RESEND=2, - - NET_CTRLMSG_KEEPALIVE=0, - NET_CTRLMSG_CONNECT=1, - NET_CTRLMSG_CONNECTACCEPT=2, - NET_CTRLMSG_ACCEPT=3, - NET_CTRLMSG_CLOSE=4, - - NET_SERVER_MAXBANS=1024, - - NET_CONN_BUFFERSIZE=1024*16, - - NET_ENUM_TERMINATOR -}; - - -typedef struct NETPACKETCONSTRUCT -{ - int flags; - int ack; - int num_chunks; - int data_size; - unsigned char chunk_data[NET_MAX_PAYLOAD]; -} NETPACKETCONSTRUCT; - -typedef struct NETCHUNKHEADER -{ - int flags; - int size; - int sequence; -} NETCHUNKHEADER; - -typedef struct -{ - int flags; - int data_size; - unsigned char *data; - - int sequence; - int64 last_send_time; - int64 first_send_time; -} NETCHUNKDATA; - -typedef struct -{ - unsigned short seq; - unsigned short ack; - unsigned state; - - int token; - int remote_closed; - - RINGBUFFER *buffer; - - int64 last_update_time; - int64 last_recv_time; - int64 last_send_time; - - char error_string[256]; - - NETPACKETCONSTRUCT construct; - - NETADDR peeraddr; - NETSOCKET socket; - NETSTATS stats; - - char buffer_memory[NET_CONN_BUFFERSIZE]; -} NETCONNECTION; - -typedef struct NETRECVINFO -{ - NETADDR addr; - NETCONNECTION *conn; - int current_chunk; - int client_id; - int valid; - NETPACKETCONSTRUCT data; - unsigned char buffer[NET_MAX_PACKETSIZE]; -} NETRECVINFO; - -/* */ - -/* connection functions */ -void conn_init(NETCONNECTION *conn, NETSOCKET socket); -int conn_connect(NETCONNECTION *conn, NETADDR *addr); -void conn_disconnect(NETCONNECTION *conn, const char *reason); -int conn_update(NETCONNECTION *conn); -int conn_feed(NETCONNECTION *conn, NETPACKETCONSTRUCT *packet, NETADDR *addr); -void conn_queue_chunk(NETCONNECTION *conn, int flags, int data_size, const void *data); -const char *conn_error(NETCONNECTION *conn); -void conn_want_resend(NETCONNECTION *conn); -int conn_flush(NETCONNECTION *conn); - -/* recvinfo functions */ -void recvinfo_clear(NETRECVINFO *info); -void recvinfo_start(NETRECVINFO *info, NETADDR *addr, NETCONNECTION *conn, int cid); -int recvinfo_fetch_chunk(NETRECVINFO *info, NETCHUNK *chunk); - -/* misc helper functions */ -/* The backroom is ack-NET_MAX_SEQUENCE/2. Used for knowing if we acked a packet or not */ -int seq_in_backroom(int seq, int ack); -void send_controlmsg(NETSOCKET socket, NETADDR *addr, int ack, int controlmsg, const void *extra, int extra_size); -void send_packet_connless(NETSOCKET socket, NETADDR *addr, const void *data, int data_size); -void send_packet(NETSOCKET socket, NETADDR *addr, NETPACKETCONSTRUCT *packet); -int unpack_packet(unsigned char *buffer, int size, NETPACKETCONSTRUCT *packet); -unsigned char *pack_chunk_header(unsigned char *data, int flags, int size, int sequence); -unsigned char *unpack_chunk_header(unsigned char *data, NETCHUNKHEADER *header); diff --git a/src/engine/e_network_server.c b/src/engine/e_network_server.c deleted file mode 100644 index 8ec65504..00000000 --- a/src/engine/e_network_server.c +++ /dev/null @@ -1,484 +0,0 @@ -#include <base/system.h> -#include "e_network.h" -#include "e_network_internal.h" - -typedef struct -{ - NETCONNECTION conn; -} NETSLOT; - -typedef struct NETBAN -{ - NETBANINFO info; - - /* hash list */ - struct NETBAN *hashnext; - struct NETBAN *hashprev; - - /* used or free list */ - struct NETBAN *next; - struct NETBAN *prev; -} NETBAN; - -#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \ - { if(first) first->prev = object; \ - object->prev = (void*)0; \ - object->next = first; \ - first = object; } - -#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \ - { object->prev = after; \ - object->next = after->next; \ - after->next = object; \ - if(object->next) \ - object->next->prev = object; \ - } - -#define MACRO_LIST_UNLINK(object, first, prev, next) \ - { if(object->next) object->next->prev = object->prev; \ - if(object->prev) object->prev->next = object->next; \ - else first = object->next; \ - object->next = 0; object->prev = 0; } - -#define MACRO_LIST_FIND(start, next, expression) \ - { while(start && !(expression)) start = start->next; } - -struct NETSERVER -{ - NETSOCKET socket; - NETSLOT slots[NET_MAX_CLIENTS]; - int max_clients; - - NETBAN *bans[256]; - NETBAN banpool[NET_SERVER_MAXBANS]; - NETBAN *banpool_firstfree; - NETBAN *banpool_firstused; - - NETFUNC_NEWCLIENT new_client; - NETFUNC_NEWCLIENT del_client; - void *user_ptr; - - NETRECVINFO recv; -}; - -NETSERVER *netserver_open(NETADDR bindaddr, int max_clients, int flags) -{ - int i; - NETSERVER *server; - NETSOCKET socket = net_udp_create(bindaddr); - if(socket == NETSOCKET_INVALID) - return 0; - - server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1); - mem_zero(server, sizeof(NETSERVER)); - server->socket = socket; - server->max_clients = max_clients; - if(server->max_clients > NET_MAX_CLIENTS) - server->max_clients = NET_MAX_CLIENTS; - if(server->max_clients < 1) - server->max_clients = 1; - - for(i = 0; i < NET_MAX_CLIENTS; i++) - conn_init(&server->slots[i].conn, server->socket); - - /* setup all pointers for bans */ - for(i = 1; i < NET_SERVER_MAXBANS-1; i++) - { - server->banpool[i].next = &server->banpool[i+1]; - server->banpool[i].prev = &server->banpool[i-1]; - } - - server->banpool[0].next = &server->banpool[1]; - server->banpool[NET_SERVER_MAXBANS-1].prev = &server->banpool[NET_SERVER_MAXBANS-2]; - server->banpool_firstfree = &server->banpool[0]; - - return server; -} - -int netserver_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user) -{ - s->new_client = new_client; - s->del_client = del_client; - s->user_ptr = user; - return 0; -} - -int netserver_max_clients(NETSERVER *s) -{ - return s->max_clients; -} - -int netserver_close(NETSERVER *s) -{ - /* TODO: implement me */ - return 0; -} - -int netserver_drop(NETSERVER *s, int client_id, const char *reason) -{ - /* TODO: insert lots of checks here */ - NETADDR addr; - netserver_client_addr(s, client_id, &addr); - - dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"", - client_id, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], - reason - ); - conn_disconnect(&s->slots[client_id].conn, reason); - - if(s->del_client) - s->del_client(client_id, s->user_ptr); - - return 0; -} - -int netserver_ban_get(NETSERVER *s, int index, NETBANINFO *info) -{ - NETBAN *ban; - for(ban = s->banpool_firstused; ban && index; ban = ban->next, index--) - {} - - if(!ban) - return 0; - *info = ban->info; - return 1; -} - -int netserver_ban_num(NETSERVER *s) -{ - int count = 0; - NETBAN *ban; - for(ban = s->banpool_firstused; ban; ban = ban->next) - count++; - return count; -} - -static void netserver_ban_remove_by_object(NETSERVER *s, NETBAN *ban) -{ - int iphash = (ban->info.addr.ip[0]+ban->info.addr.ip[1]+ban->info.addr.ip[2]+ban->info.addr.ip[3])&0xff; - dbg_msg("netserver", "removing ban on %d.%d.%d.%d", - ban->info.addr.ip[0], ban->info.addr.ip[1], ban->info.addr.ip[2], ban->info.addr.ip[3]); - MACRO_LIST_UNLINK(ban, s->banpool_firstused, prev, next); - MACRO_LIST_UNLINK(ban, s->bans[iphash], hashprev, hashnext); - MACRO_LIST_LINK_FIRST(ban, s->banpool_firstfree, prev, next); -} - -int netserver_ban_remove(NETSERVER *s, NETADDR addr) -{ - int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; - NETBAN *ban = s->bans[iphash]; - - MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0); - - if(ban) - { - netserver_ban_remove_by_object(s, ban); - return 0; - } - - return -1; -} - -int netserver_ban_add(NETSERVER *s, NETADDR addr, int seconds) -{ - int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; - unsigned stamp = 0xffffffff; - NETBAN *ban; - - /* remove the port */ - addr.port = 0; - - if(seconds) - stamp = time_timestamp() + seconds; - - /* search to see if it already exists */ - ban = s->bans[iphash]; - MACRO_LIST_FIND(ban, hashnext, net_addr_comp(&ban->info.addr, &addr) == 0); - if(ban) - { - /* adjust the ban */ - ban->info.expires = stamp; - return 0; - } - - if(!s->banpool_firstfree) - return -1; - - /* fetch and clear the new ban */ - ban = s->banpool_firstfree; - MACRO_LIST_UNLINK(ban, s->banpool_firstfree, prev, next); - - /* setup the ban info */ - ban->info.expires = stamp; - ban->info.addr = addr; - - /* add it to the ban hash */ - MACRO_LIST_LINK_FIRST(ban, s->bans[iphash], hashprev, hashnext); - - /* insert it into the used list */ - { - if(s->banpool_firstused) - { - NETBAN *insert_after = s->banpool_firstused; - MACRO_LIST_FIND(insert_after, next, stamp < insert_after->info.expires); - - if(insert_after) - insert_after = insert_after->prev; - else - { - /* add to last */ - insert_after = s->banpool_firstused; - while(insert_after->next) - insert_after = insert_after->next; - } - - if(insert_after) - { - MACRO_LIST_LINK_AFTER(ban, insert_after, prev, next); - } - else - { - MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next); - } - } - else - { - MACRO_LIST_LINK_FIRST(ban, s->banpool_firstused, prev, next); - } - } - - /* drop banned clients */ - { - char buf[128]; - int i; - NETADDR banaddr; - - if(seconds) - str_format(buf, sizeof(buf), "you have been banned for %d minutes", seconds/60); - else - str_format(buf, sizeof(buf), "you have been banned for life"); - - for(i = 0; i < s->max_clients; i++) - { - banaddr = s->slots[i].conn.peeraddr; - banaddr.port = 0; - - if(net_addr_comp(&addr, &banaddr) == 0) - netserver_drop(s, i, buf); - } - } - return 0; -} - -int netserver_update(NETSERVER *s) -{ - unsigned now = time_timestamp(); - - int i; - for(i = 0; i < s->max_clients; i++) - { - conn_update(&s->slots[i].conn); - if(s->slots[i].conn.state == NET_CONNSTATE_ERROR) - netserver_drop(s, i, conn_error(&s->slots[i].conn)); - } - - /* remove expired bans */ - while(s->banpool_firstused && s->banpool_firstused->info.expires < now) - { - NETBAN *ban = s->banpool_firstused; - netserver_ban_remove_by_object(s, ban); - } - - (void)now; - - return 0; -} - -/* - TODO: chopp up this function into smaller working parts -*/ -int netserver_recv(NETSERVER *s, NETCHUNK *chunk) -{ - unsigned now = time_timestamp(); - - while(1) - { - NETADDR addr; - int i, bytes, found; - - /* check for a chunk */ - if(recvinfo_fetch_chunk(&s->recv, chunk)) - return 1; - - /* TODO: empty the recvinfo */ - bytes = net_udp_recv(s->socket, &addr, s->recv.buffer, NET_MAX_PACKETSIZE); - - /* no more packets for now */ - if(bytes <= 0) - break; - - if(unpack_packet(s->recv.buffer, bytes, &s->recv.data) == 0) - { - NETBAN *ban = 0; - NETADDR banaddr = addr; - int iphash = (addr.ip[0]+addr.ip[1]+addr.ip[2]+addr.ip[3])&0xff; - found = 0; - banaddr.port = 0; - - /* search a ban */ - for(ban = s->bans[iphash]; ban; ban = ban->hashnext) - { - if(net_addr_comp(&ban->info.addr, &banaddr) == 0) - break; - } - - /* check if we just should drop the packet */ - if(ban) - { - // banned, reply with a message - char banstr[128]; - if(ban->info.expires) - { - int mins = ((ban->info.expires - now)+59)/60; - if(mins == 1) - str_format(banstr, sizeof(banstr), "banned for %d minute", mins); - else - str_format(banstr, sizeof(banstr), "banned for %d minutes", mins); - } - else - str_format(banstr, sizeof(banstr), "banned for life"); - send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, banstr, str_length(banstr)+1); - continue; - } - - if(s->recv.data.flags&NET_PACKETFLAG_CONNLESS) - { - chunk->flags = NETSENDFLAG_CONNLESS; - chunk->client_id = -1; - chunk->address = addr; - chunk->data_size = s->recv.data.data_size; - chunk->data = s->recv.data.chunk_data; - return 1; - } - else - { - /* TODO: check size here */ - if(s->recv.data.flags&NET_PACKETFLAG_CONTROL && s->recv.data.chunk_data[0] == NET_CTRLMSG_CONNECT) - { - found = 0; - - /* check if we already got this client */ - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state != NET_CONNSTATE_OFFLINE && - net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - found = 1; /* silent ignore.. we got this client already */ - break; - } - } - - /* client that wants to connect */ - if(!found) - { - for(i = 0; i < s->max_clients; i++) - { - if(s->slots[i].conn.state == NET_CONNSTATE_OFFLINE) - { - found = 1; - conn_feed(&s->slots[i].conn, &s->recv.data, &addr); - if(s->new_client) - s->new_client(i, s->user_ptr); - break; - } - } - - if(!found) - { - const char fullmsg[] = "server is full"; - send_controlmsg(s->socket, &addr, 0, NET_CTRLMSG_CLOSE, fullmsg, sizeof(fullmsg)); - } - } - } - else - { - /* normal packet, find matching slot */ - for(i = 0; i < s->max_clients; i++) - { - if(net_addr_comp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - if(conn_feed(&s->slots[i].conn, &s->recv.data, &addr)) - { - if(s->recv.data.data_size) - recvinfo_start(&s->recv, &addr, &s->slots[i].conn, i); - } - } - } - } - } - } - } - return 0; -} - -int netserver_send(NETSERVER *s, NETCHUNK *chunk) -{ - if(chunk->data_size >= NET_MAX_PAYLOAD) - { - dbg_msg("netserver", "packet payload too big. %d. dropping packet", chunk->data_size); - return -1; - } - - if(chunk->flags&NETSENDFLAG_CONNLESS) - { - /* send connectionless packet */ - send_packet_connless(s->socket, &chunk->address, chunk->data, chunk->data_size); - } - else - { - int f = 0; - dbg_assert(chunk->client_id >= 0, "errornous client id"); - dbg_assert(chunk->client_id < s->max_clients, "errornous client id"); - - if(chunk->flags&NETSENDFLAG_VITAL) - f = NET_CHUNKFLAG_VITAL; - - conn_queue_chunk(&s->slots[chunk->client_id].conn, f, chunk->data_size, chunk->data); - - if(chunk->flags&NETSENDFLAG_FLUSH) - conn_flush(&s->slots[chunk->client_id].conn); - } - return 0; -} - -void netserver_stats(NETSERVER *s, NETSTATS *stats) -{ - int num_stats = sizeof(NETSTATS)/sizeof(int); - int *istats = (int *)stats; - int c, i; - - mem_zero(stats, sizeof(NETSTATS)); - - for(c = 0; c < s->max_clients; c++) - { - if(s->slots[c].conn.state != NET_CONNSTATE_OFFLINE) - { - int *sstats = (int *)(&(s->slots[c].conn.stats)); - for(i = 0; i < num_stats; i++) - istats[i] += sstats[i]; - } - } -} - -NETSOCKET netserver_socket(NETSERVER *s) -{ - return s->socket; -} - - -int netserver_client_addr(NETSERVER *s, int client_id, NETADDR *addr) -{ - *addr = s->slots[client_id].conn.peeraddr; - return 1; -} diff --git a/src/engine/e_network_server.cpp b/src/engine/e_network_server.cpp new file mode 100644 index 00000000..995290ef --- /dev/null +++ b/src/engine/e_network_server.cpp @@ -0,0 +1,408 @@ +#include <base/system.h> +#include "e_network.h" + +#define MACRO_LIST_LINK_FIRST(object, first, prev, next) \ + { if(first) first->prev = object; \ + object->prev = (struct CBan *)0; \ + object->next = first; \ + first = object; } + +#define MACRO_LIST_LINK_AFTER(object, after, prev, next) \ + { object->prev = after; \ + object->next = after->next; \ + after->next = object; \ + if(object->next) \ + object->next->prev = object; \ + } + +#define MACRO_LIST_UNLINK(object, first, prev, next) \ + { if(object->next) object->next->prev = object->prev; \ + if(object->prev) object->prev->next = object->next; \ + else first = object->next; \ + object->next = 0; object->prev = 0; } + +#define MACRO_LIST_FIND(start, next, expression) \ + { while(start && !(expression)) start = start->next; } + +bool CNetServer::Open(NETADDR BindAddr, int MaxClients, int Flags) +{ + // zero out the whole structure + mem_zero(this, sizeof(*this)); + + // open socket + m_Socket = net_udp_create(BindAddr); + if(m_Socket == NETSOCKET_INVALID) + return false; + + // clamp clients + m_MaxClients = MaxClients; + if(m_MaxClients > NET_MAX_CLIENTS) + m_MaxClients = NET_MAX_CLIENTS; + if(m_MaxClients < 1) + m_MaxClients = 1; + + for(int i = 0; i < NET_MAX_CLIENTS; i++) + m_aSlots[i].m_Connection.Init(m_Socket); + + /* setup all pointers for bans */ + for(int i = 1; i < NET_SERVER_MAXBANS-1; i++) + { + m_BanPool[i].m_pNext = &m_BanPool[i+1]; + m_BanPool[i].m_pPrev = &m_BanPool[i-1]; + } + + m_BanPool[0].m_pNext = &m_BanPool[1]; + m_BanPool[NET_SERVER_MAXBANS-1].m_pPrev = &m_BanPool[NET_SERVER_MAXBANS-2]; + m_BanPool_FirstFree = &m_BanPool[0]; + + return true; +} + +int CNetServer::SetCallbacks(NETFUNC_NEWCLIENT pfnNewClient, NETFUNC_DELCLIENT pfnDelClient, void *pUser) +{ + m_pfnNewClient = pfnNewClient; + m_pfnDelClient = pfnDelClient; + m_UserPtr = pUser; + return 0; +} + +int CNetServer::Close() +{ + /* TODO: implement me */ + return 0; +} + +int CNetServer::Drop(int ClientID, const char *pReason) +{ + /* TODO: insert lots of checks here */ + NETADDR Addr = ClientAddr(ClientID); + + dbg_msg("net_server", "client dropped. cid=%d ip=%d.%d.%d.%d reason=\"%s\"", + ClientID, + Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], + pReason + ); + + m_aSlots[ClientID].m_Connection.Disconnect(pReason); + + if(m_pfnDelClient) + m_pfnDelClient(ClientID, m_UserPtr); + + return 0; +} + +int CNetServer::BanGet(int Index, CBanInfo *pInfo) +{ + CBan *pBan; + for(pBan = m_BanPool_FirstUsed; pBan && Index; pBan = pBan->m_pNext, Index--) + {} + + if(!pBan) + return 0; + *pInfo = pBan->m_Info; + return 1; +} + +int CNetServer::BanNum() +{ + int Count = 0; + CBan *pBan; + for(pBan = m_BanPool_FirstUsed; pBan; pBan = pBan->m_pNext) + Count++; + return Count; +} + +void CNetServer::BanRemoveByObject(CBan *pBan) +{ + int iphash = (pBan->m_Info.m_Addr.ip[0]+pBan->m_Info.m_Addr.ip[1]+pBan->m_Info.m_Addr.ip[2]+pBan->m_Info.m_Addr.ip[3])&0xff; + dbg_msg("netserver", "removing ban on %d.%d.%d.%d", + pBan->m_Info.m_Addr.ip[0], pBan->m_Info.m_Addr.ip[1], pBan->m_Info.m_Addr.ip[2], pBan->m_Info.m_Addr.ip[3]); + MACRO_LIST_UNLINK(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); + MACRO_LIST_UNLINK(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext); + MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); +} + +int CNetServer::BanRemove(NETADDR Addr) +{ + int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; + CBan *pBan = m_aBans[iphash]; + + MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); + + if(pBan) + { + BanRemoveByObject(pBan); + return 0; + } + + return -1; +} + +int CNetServer::BanAdd(NETADDR Addr, int Seconds) +{ + int iphash = (Addr.ip[0]+Addr.ip[1]+Addr.ip[2]+Addr.ip[3])&0xff; + int Stamp = -1; + CBan *pBan; + + /* remove the port */ + Addr.port = 0; + + if(Seconds) + Stamp = time_timestamp() + Seconds; + + /* search to see if it already exists */ + pBan = m_aBans[iphash]; + MACRO_LIST_FIND(pBan, m_pHashNext, net_addr_comp(&pBan->m_Info.m_Addr, &Addr) == 0); + if(pBan) + { + /* adjust the ban */ + pBan->m_Info.m_Expires = Stamp; + return 0; + } + + if(!m_BanPool_FirstFree) + return -1; + + /* fetch and clear the new ban */ + pBan = m_BanPool_FirstFree; + MACRO_LIST_UNLINK(pBan, m_BanPool_FirstFree, m_pPrev, m_pNext); + + /* setup the ban info */ + pBan->m_Info.m_Expires = Stamp; + pBan->m_Info.m_Addr = Addr; + + /* add it to the ban hash */ + MACRO_LIST_LINK_FIRST(pBan, m_aBans[iphash], m_pHashPrev, m_pHashNext); + + /* insert it into the used list */ + { + if(m_BanPool_FirstUsed) + { + CBan *pInsertAfter = m_BanPool_FirstUsed; + MACRO_LIST_FIND(pInsertAfter, m_pNext, Stamp < pInsertAfter->m_Info.m_Expires); + + if(pInsertAfter) + pInsertAfter = pInsertAfter->m_pPrev; + else + { + /* add to last */ + pInsertAfter = m_BanPool_FirstUsed; + while(pInsertAfter->m_pNext) + pInsertAfter = pInsertAfter->m_pNext; + } + + if(pInsertAfter) + { + MACRO_LIST_LINK_AFTER(pBan, pInsertAfter, m_pPrev, m_pNext); + } + else + { + MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); + } + } + else + { + MACRO_LIST_LINK_FIRST(pBan, m_BanPool_FirstUsed, m_pPrev, m_pNext); + } + } + + /* drop banned clients */ + { + char Buf[128]; + NETADDR BanAddr; + + if(Seconds) + str_format(Buf, sizeof(Buf), "you have been banned for %d minutes", Seconds/60); + else + str_format(Buf, sizeof(Buf), "you have been banned for life"); + + for(int i = 0; i < MaxClients(); i++) + { + BanAddr = m_aSlots[i].m_Connection.PeerAddress(); + BanAddr.port = 0; + + if(net_addr_comp(&Addr, &BanAddr) == 0) + Drop(i, Buf); + } + } + return 0; +} + +int CNetServer::Update() +{ + int Now = time_timestamp(); + for(int i = 0; i < MaxClients(); i++) + { + m_aSlots[i].m_Connection.Update(); + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_ERROR) + Drop(i, m_aSlots[i].m_Connection.ErrorString()); + } + + /* remove expired bans */ + while(m_BanPool_FirstUsed && m_BanPool_FirstUsed->m_Info.m_Expires < Now) + { + CBan *pBan = m_BanPool_FirstUsed; + BanRemoveByObject(pBan); + } + + return 0; +} + +/* + TODO: chopp up this function into smaller working parts +*/ +int CNetServer::Recv(CNetChunk *pChunk) +{ + unsigned now = time_timestamp(); + + while(1) + { + NETADDR Addr; + + /* check for a chunk */ + if(m_RecvUnpacker.FetchChunk(pChunk)) + return 1; + + /* TODO: empty the recvinfo */ + int Bytes = net_udp_recv(m_Socket, &Addr, m_RecvUnpacker.m_aBuffer, NET_MAX_PACKETSIZE); + + /* no more packets for now */ + if(Bytes <= 0) + break; + + if(CNetBase::UnpackPacket(m_RecvUnpacker.m_aBuffer, Bytes, &m_RecvUnpacker.m_Data) == 0) + { + CBan *pBan = 0; + NETADDR BanAddr = Addr; + int iphash = (BanAddr.ip[0]+BanAddr.ip[1]+BanAddr.ip[2]+BanAddr.ip[3])&0xff; + int Found = 0; + BanAddr.port = 0; + + /* search a ban */ + for(pBan = m_aBans[iphash]; pBan; pBan = pBan->m_pHashNext) + { + if(net_addr_comp(&pBan->m_Info.m_Addr, &BanAddr) == 0) + break; + } + + /* check if we just should drop the packet */ + if(pBan) + { + // banned, reply with a message + char BanStr[128]; + if(pBan->m_Info.m_Expires) + { + int Mins = ((pBan->m_Info.m_Expires - now)+59)/60; + if(Mins == 1) + str_format(BanStr, sizeof(BanStr), "banned for %d minute", Mins); + else + str_format(BanStr, sizeof(BanStr), "banned for %d minutes", Mins); + } + else + str_format(BanStr, sizeof(BanStr), "banned for life"); + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, BanStr, str_length(BanStr)+1); + continue; + } + + if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONNLESS) + { + pChunk->m_Flags = NETSENDFLAG_CONNLESS; + pChunk->m_ClientID = -1; + pChunk->m_Address = Addr; + pChunk->m_DataSize = m_RecvUnpacker.m_Data.m_DataSize; + pChunk->m_pData = m_RecvUnpacker.m_Data.m_aChunkData; + return 1; + } + else + { + /* TODO: check size here */ + if(m_RecvUnpacker.m_Data.m_Flags&NET_PACKETFLAG_CONTROL && m_RecvUnpacker.m_Data.m_aChunkData[0] == NET_CTRLMSG_CONNECT) + { + Found = 0; + + /* check if we already got this client */ + for(int i = 0; i < MaxClients(); i++) + { + NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); + if(m_aSlots[i].m_Connection.State() != NET_CONNSTATE_OFFLINE && + net_addr_comp(&PeerAddr, &Addr) == 0) + { + Found = 1; /* silent ignore.. we got this client already */ + break; + } + } + + /* client that wants to connect */ + if(!Found) + { + for(int i = 0; i < MaxClients(); i++) + { + if(m_aSlots[i].m_Connection.State() == NET_CONNSTATE_OFFLINE) + { + Found = 1; + m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr); + if(m_pfnNewClient) + m_pfnNewClient(i, m_UserPtr); + break; + } + } + + if(!Found) + { + const char FullMsg[] = "server is full"; + CNetBase::SendControlMsg(m_Socket, &Addr, 0, NET_CTRLMSG_CLOSE, FullMsg, sizeof(FullMsg)); + } + } + } + else + { + /* normal packet, find matching slot */ + for(int i = 0; i < MaxClients(); i++) + { + NETADDR PeerAddr = m_aSlots[i].m_Connection.PeerAddress(); + if(net_addr_comp(&PeerAddr, &Addr) == 0) + { + if(m_aSlots[i].m_Connection.Feed(&m_RecvUnpacker.m_Data, &Addr)) + { + if(m_RecvUnpacker.m_Data.m_DataSize) + m_RecvUnpacker.Start(&Addr, &m_aSlots[i].m_Connection, i); + } + } + } + } + } + } + } + return 0; +} + +int CNetServer::Send(CNetChunk *pChunk) +{ + if(pChunk->m_DataSize >= NET_MAX_PAYLOAD) + { + dbg_msg("netserver", "packet payload too big. %d. dropping packet", pChunk->m_DataSize); + return -1; + } + + if(pChunk->m_Flags&NETSENDFLAG_CONNLESS) + { + /* send connectionless packet */ + CNetBase::SendPacketConnless(m_Socket, &pChunk->m_Address, pChunk->m_pData, pChunk->m_DataSize); + } + else + { + int Flags = 0; + dbg_assert(pChunk->m_ClientID >= 0, "errornous client id"); + dbg_assert(pChunk->m_ClientID < MaxClients(), "errornous client id"); + + if(pChunk->m_Flags&NETSENDFLAG_VITAL) + Flags = NET_CHUNKFLAG_VITAL; + + m_aSlots[pChunk->m_ClientID].m_Connection.QueueChunk(Flags, pChunk->m_DataSize, pChunk->m_pData); + + if(pChunk->m_Flags&NETSENDFLAG_FLUSH) + m_aSlots[pChunk->m_ClientID].m_Connection.Flush(); + } + return 0; +} + diff --git a/src/engine/e_packer.c b/src/engine/e_packer.c deleted file mode 100644 index aee08aed..00000000 --- a/src/engine/e_packer.c +++ /dev/null @@ -1,213 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdlib.h> /* rand() */ -#include <base/system.h> - -#include "e_packer.h" -#include "e_compression.h" -#include "e_engine.h" -#include "e_config.h" - -/* useful for debugging */ -#if 0 - #define packing_error(p) p->error = 1; dbg_break() -#else - #define packing_error(p) p->error = 1 -#endif - -static int stress_get_int() -{ - static const int nasty[] = {-1, 0, 1, 66000, -66000, (-1<<31), 0x7fffffff}; - if(rand()&1) - return rand(); - return nasty[rand()%6]; -} - -static const char *stress_get_string(int *size) -{ - static char noise[1024]; - int i; - int s; - s = (rand()%1024)-1; - for(i = 0; i < s; i++) - noise[i] = (rand()%254)+1; - noise[s] = 0; - if(size) - *size = s; - return noise; -} - - -static int stress_prob(float probability) -{ - if(!config.dbg_stress_network) - return 0; - if(rand()/(float)RAND_MAX < probability) - return 1; - return 0; -} - - -void packer_reset(PACKER *p) -{ - p->error = 0; - p->current = p->buffer; - p->end = p->current + PACKER_BUFFER_SIZE; -} - -void packer_add_int(PACKER *p, int i) -{ - if(p->error) - return; - - if(stress_prob(0.025f)) - i = stress_get_int(); - - /* make sure that we have space enough */ - if(p->end - p->current < 6) - { - dbg_break(); - p->error = 1; - } - else - p->current = vint_pack(p->current, i); -} - -void packer_add_string(PACKER *p, const char *str, int limit) -{ - if(p->error) - return; - - if(stress_prob(0.1f)) - { - str = stress_get_string(0); - limit = 0; - } - - /* */ - if(limit > 0) - { - while(*str && limit != 0) - { - *p->current++ = *str++; - limit--; - - if(p->current >= p->end) - { - packing_error(p); - break; - } - } - *p->current++ = 0; - } - else - { - while(*str) - { - *p->current++ = *str++; - - if(p->current >= p->end) - { - packing_error(p); - break; - } - } - *p->current++ = 0; - } -} - -void packer_add_raw(PACKER *p, const unsigned char *data, int size) -{ - if(p->error) - return; - - if(p->current+size >= p->end) - { - packing_error(p); - return; - } - - while(size) - { - *p->current++ = *data++; - size--; - } -} - -int packer_size(PACKER *p) -{ - return (const unsigned char *)p->current-(const unsigned char *)p->buffer; -} - -const unsigned char *packer_data(PACKER *p) -{ - return (const unsigned char *)p->buffer; -} - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size) -{ - p->error = 0; - p->start = data; - p->end = p->start + size; - p->current = p->start; -} - -int unpacker_get_int(UNPACKER *p) -{ - int i; - if(p->error) - return 0; - if(p->current >= p->end) - { - packing_error(p); - return 0; - } - - p->current = vint_unpack(p->current, &i); - if(p->current > p->end) - { - packing_error(p); - return 0; - } - return i; -} - -const char *unpacker_get_string(UNPACKER *p) -{ - char *ptr; - if(p->error || p->current >= p->end) - return ""; - - ptr = (char *)p->current; - while(*p->current) /* skip the string */ - { - p->current++; - if(p->current == p->end) - { - packing_error(p); - return ""; - } - } - p->current++; - - /* sanitize all strings */ - str_sanitize(ptr); - return ptr; -} - -const unsigned char *unpacker_get_raw(UNPACKER *p, int size) -{ - const unsigned char *ptr = p->current; - if(p->error) - return 0; - - /* check for nasty sizes */ - if(size < 0 || p->current+size > p->end) - { - packing_error(p); - return 0; - } - - /* "unpack" the data */ - p->current += size; - return ptr; -} diff --git a/src/engine/e_packer.cpp b/src/engine/e_packer.cpp new file mode 100644 index 00000000..0d8aeab3 --- /dev/null +++ b/src/engine/e_packer.cpp @@ -0,0 +1,155 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include <stdlib.h> /* rand() */ +#include <base/system.h> + +#include "e_packer.h" +#include "e_compression.h" +#include "e_engine.h" +#include "e_config.h" + +void CPacker::Reset() +{ + m_Error = 0; + m_pCurrent = m_aBuffer; + m_pEnd = m_pCurrent + PACKER_BUFFER_SIZE; +} + +void CPacker::AddInt(int i) +{ + if(m_Error) + return; + + /* make sure that we have space enough */ + if(m_pEnd - m_pCurrent < 6) + { + dbg_break(); + m_Error = 1; + } + else + m_pCurrent = vint_pack(m_pCurrent, i); +} + +void CPacker::AddString(const char *pStr, int Limit) +{ + if(m_Error) + return; + + /* */ + if(Limit > 0) + { + while(*pStr && Limit != 0) + { + *m_pCurrent++ = *pStr++; + Limit--; + + if(m_pCurrent >= m_pEnd) + { + m_Error = 1; + break; + } + } + *m_pCurrent++ = 0; + } + else + { + while(*pStr) + { + *m_pCurrent++ = *pStr++; + + if(m_pCurrent >= m_pEnd) + { + m_Error = 1; + break; + } + } + *m_pCurrent++ = 0; + } +} + +void CPacker::AddRaw(const unsigned char *pData, int Size) +{ + if(m_Error) + return; + + if(m_pCurrent+Size >= m_pEnd) + { + m_Error = 1; + return; + } + + while(Size) + { + *m_pCurrent++ = *pData++; + Size--; + } +} + + +void CUnpacker::Reset(const unsigned char *pData, int Size) +{ + m_Error = 0; + m_pStart = pData; + m_pEnd = m_pStart + Size; + m_pCurrent = m_pStart; +} + +int CUnpacker::GetInt() +{ + if(m_Error) + return 0; + + if(m_pCurrent >= m_pEnd) + { + m_Error = 1; + return 0; + } + + int i; + m_pCurrent = vint_unpack(m_pCurrent, &i); + if(m_pCurrent > m_pEnd) + { + m_Error = 1; + return 0; + } + return i; +} + +const char *CUnpacker::GetString() +{ + if(m_Error || m_pCurrent >= m_pEnd) + return ""; + + char *pPtr = (char *)m_pCurrent; + while(*m_pCurrent) /* skip the string */ + { + m_pCurrent++; + if(m_pCurrent == m_pEnd) + { + m_Error = 1;; + return ""; + } + } + m_pCurrent++; + + /* sanitize all strings */ + str_sanitize(pPtr); + return pPtr; +} + +const unsigned char *CUnpacker::GetRaw(int Size) +{ + const unsigned char *pPtr = m_pCurrent; + if(m_Error) + return 0; + + /* check for nasty sizes */ + if(Size < 0 || m_pCurrent+Size > m_pEnd) + { + m_Error = 1; + return 0; + } + + /* "unpack" the data */ + m_pCurrent += Size; + return pPtr; +} diff --git a/src/engine/e_packer.h b/src/engine/e_packer.h index 71b99ff7..5dc80e7a 100644 --- a/src/engine/e_packer.h +++ b/src/engine/e_packer.h @@ -1,35 +1,37 @@ /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -enum +class CPacker { - PACKER_BUFFER_SIZE=1024*2 -}; + enum + { + PACKER_BUFFER_SIZE=1024*2 + }; -typedef struct -{ + unsigned char m_aBuffer[PACKER_BUFFER_SIZE]; + unsigned char *m_pCurrent; + unsigned char *m_pEnd; + int m_Error; +public: + void Reset(); + void AddInt(int i); + void AddString(const char *pStr, int Limit); + void AddRaw(const unsigned char *pData, int Size); - unsigned char buffer[PACKER_BUFFER_SIZE]; - unsigned char *current; - unsigned char *end; - int error; -} PACKER; + int Size() const { return (int)(m_pCurrent-m_aBuffer); } + const unsigned char *Data() const { return m_aBuffer; } + bool Error() const { return m_Error; } +}; -typedef struct +class CUnpacker { - const unsigned char *current; - const unsigned char *start; - const unsigned char *end; - int error; -} UNPACKER; - -void packer_reset(PACKER *p); -void packer_add_int(PACKER *p, int i); -void packer_add_string(PACKER *p, const char *str, int limit); -void packer_add_raw(PACKER *p, const unsigned char *data, int size); -int packer_size(PACKER *p); -const unsigned char *packer_data(PACKER *p); - -void unpacker_reset(UNPACKER *p, const unsigned char *data, int size); -int unpacker_get_int(UNPACKER *p); -const char *unpacker_get_string(UNPACKER *p); -const unsigned char *unpacker_get_raw(UNPACKER *p, int size); + const unsigned char *m_pStart; + const unsigned char *m_pCurrent; + const unsigned char *m_pEnd; + int m_Error; +public: + void Reset(const unsigned char *pData, int Size); + int GetInt(); + const char *GetString(); + const unsigned char *GetRaw(int Size); + bool Error() const { return m_Error; } +}; diff --git a/src/engine/e_ringbuffer.c b/src/engine/e_ringbuffer.c deleted file mode 100644 index 39dfa3e9..00000000 --- a/src/engine/e_ringbuffer.c +++ /dev/null @@ -1,362 +0,0 @@ -#include <base/system.h> - -#include "e_ringbuffer.h" - -typedef struct RBITEM -{ - struct RBITEM *prev; - struct RBITEM *next; - int free; - int size; -} RBITEM; - -/* - -*/ -struct RINGBUFFER -{ - RBITEM *produce; - RBITEM *consume; - - RBITEM *first; - RBITEM *last; - void *memory; - int size; - int flags; -}; - -RINGBUFFER *ringbuf_init(void *memory, int size, int flags) -{ - RINGBUFFER *rb = (RINGBUFFER *)memory; - mem_zero(memory, size); - - rb->memory = rb+1; - rb->size = (size-sizeof(RINGBUFFER))/sizeof(RBITEM)*sizeof(RBITEM); - rb->first = (RBITEM *)rb->memory; - rb->first->free = 1; - rb->first->size = rb->size; - rb->last = rb->first; - rb->produce = rb->first; - rb->consume = rb->first; - - rb->flags = flags; - - return rb; -} - -static RBITEM *ringbuf_nextblock(RINGBUFFER *rb, RBITEM *item) -{ - if(item->next) - return item->next; - return rb->first; -} - -static RBITEM *ringbuf_prevblock(RINGBUFFER *rb, RBITEM *item) -{ - if(item->prev) - return item->prev; - return rb->last; -} - -static RBITEM *ringbuf_mergeback(RINGBUFFER *rb, RBITEM *item) -{ - /* make sure that this block and previous block is free */ - if(!item->free || !item->prev || !item->prev->free) - return item; - - /* merge the blocks */ - item->prev->size += item->size; - item->prev->next = item->next; - - /* fixup pointers */ - if(item->next) - item->next->prev = item->prev; - else - rb->last = item->prev; - - if(item == rb->produce) - rb->produce = item->prev; - - if(item == rb->consume) - rb->consume = item->prev; - - /* return the current block */ - return item->prev; -} - -int ringbuf_popfirst(RINGBUFFER *rb) -{ - if(rb->consume->free) - return 0; - - /* set the free flag */ - rb->consume->free = 1; - - /* previous block is also free, merge them */ - rb->consume = ringbuf_mergeback(rb, rb->consume); - - /* advance the consume pointer */ - rb->consume = ringbuf_nextblock(rb, rb->consume); - while(rb->consume->free && rb->consume != rb->produce) - { - rb->consume = ringbuf_mergeback(rb, rb->consume); - rb->consume = ringbuf_nextblock(rb, rb->consume); - } - - /* in the case that we have catched up with the produce pointer */ - /* we might stand on a free block so merge em */ - ringbuf_mergeback(rb, rb->consume); - return 1; -} - -void *ringbuf_allocate(RINGBUFFER *rb, int size) -{ - int wanted_size = (size+sizeof(RBITEM)+sizeof(RBITEM)-1)/sizeof(RBITEM)*sizeof(RBITEM); - RBITEM *block = 0; - - /* check if we even can fit this block */ - if(wanted_size > rb->size) - return 0; - - while(1) - { - /* check for space */ - if(rb->produce->free) - { - if(rb->produce->size >= wanted_size) - block = rb->produce; - else - { - /* wrap around to try to find a block */ - if(rb->first->free && rb->first->size >= wanted_size) - block = rb->first; - } - } - - if(block) - break; - else - { - /* we have no block, check our policy and see what todo */ - if(rb->flags&RINGBUF_FLAG_RECYCLE) - { - if(!ringbuf_popfirst(rb)) - return 0; - } - else - return 0; - } - } - - /* okey, we have our block */ - - /* split the block if needed */ - if(block->size > wanted_size) - { - RBITEM *new_item = (RBITEM *)((char *)block + wanted_size); - new_item->prev = block; - new_item->next = block->next; - if(new_item->next) - new_item->next->prev = new_item; - block->next = new_item; - - new_item->free = 1; - new_item->size = block->size - wanted_size; - block->size = wanted_size; - - if(!new_item->next) - rb->last = new_item; - } - - - /* set next block */ - rb->produce = ringbuf_nextblock(rb, block); - - /* set as used and return the item pointer */ - block->free = 0; - return block+1; -} - -void *ringbuf_prev(RINGBUFFER *rb, void *current) -{ - RBITEM *item = ((RBITEM *)current) - 1; - - while(1) - { - item = ringbuf_prevblock(rb, item); - if(item == rb->produce) - return 0; - if(!item->free) - return item+1; - } -} - -void *ringbuf_next(RINGBUFFER *rb, void *current) -{ - RBITEM *item = ((RBITEM *)current) - 1; - - while(1) - { - item = ringbuf_nextblock(rb, item); - if(item == rb->produce) - return 0; - if(!item->free) - return item+1; - } -} - -void *ringbuf_first(RINGBUFFER *rb) -{ - if(rb->consume->free) - return 0; - return rb->consume+1; -} - -void *ringbuf_last(RINGBUFFER *rb) -{ - if(!rb->produce->free) - return rb->produce+1; - return ringbuf_prev(rb, rb->produce+1); -} - - -/* debugging and testing stuff */ - -static void ringbuf_debugdump(RINGBUFFER *rb, const char *msg) -{ - RBITEM *cur = rb->first; - - dbg_msg("ringbuf", "-- dumping --"); - - while(cur) - { - char flags[4] = " "; - if(cur->free) - flags[0] = 'F'; - if(cur == rb->consume) - flags[1] = '>'; - if(cur == rb->produce) - flags[2] = '<'; - dbg_msg("ringbuf", "%s %d", flags, cur->size); - cur = cur->next; - } - - dbg_msg("ringbuf", "-- --"); - - if(msg) - dbg_assert(0, msg); -} - - - -static void ringbuf_validate(RINGBUFFER *rb) -{ - RBITEM *prev = 0; - RBITEM *cur = rb->first; - int freechunks = 0; - int got_consume = 0; - int got_produce = 0; - - while(cur) - { - - if(cur->free) - freechunks++; - - if(freechunks > 2) ringbuf_debugdump(rb, "too many free chunks"); - if(prev && prev->free && cur->free) ringbuf_debugdump(rb, "two free chunks next to each other"); - if(cur == rb->consume) got_consume = 1; - if(cur == rb->produce) got_produce = 1; - - dbg_assert(cur->prev == prev, "prev pointers doesn't match"); - dbg_assert(!prev || prev->next == cur, "next pointers doesn't match"); - dbg_assert(cur->next || cur == rb->last, "last isn't last"); - - prev = cur; - cur = cur->next; - } - - if(!got_consume) ringbuf_debugdump(rb, "consume pointer isn't pointing at a valid block"); - if(!got_produce) ringbuf_debugdump(rb, "produce pointer isn't pointing at a valid block"); -} - -int ringbuf_test() -{ - char buffer[256]; - RINGBUFFER *rb; - int i, s, k, m; - int count; - int testcount = 0; - - void *item; - int before; - - - for(k = 100; k < sizeof(buffer); k++) - { - if((k%10) == 0) - dbg_msg("ringbuf", "testing at %d", k); - rb = ringbuf_init(buffer, k, 0); - count = 0; - - for(s = 1; s < sizeof(buffer); s++) - { - for(i = 0; i < k*8; i++, testcount++) - { - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - before = m; - - if(ringbuf_allocate(rb, s)) - { - count++; - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(before+1 != m) ringbuf_debugdump(rb, "alloc error"); - if(count != m) ringbuf_debugdump(rb, "count error"); - } - else - { - if(ringbuf_popfirst(rb)) - { - count--; - - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(before-1 != m) dbg_msg("", "popping error %d %d", before, m); - if(count != m) ringbuf_debugdump(rb, "count error"); - } - } - - /* remove an item every 10 */ - if((i%10) == 0) - { - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - before = m; - - if(ringbuf_popfirst(rb)) - { - count--; - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(before-1 != m) dbg_msg("", "popping error %d %d", before, m); - dbg_assert(count == m, "count error"); - } - } - - /* count items */ - for(m = 0, item = ringbuf_first(rb); item; item = ringbuf_next(rb, item), m++); - if(m != count) ringbuf_debugdump(rb, "wrong number of items during forward count"); - - for(m = 0, item = ringbuf_last(rb); item; item = ringbuf_prev(rb, item), m++); - if(m != count) ringbuf_debugdump(rb, "wrong number of items during backward count"); - - ringbuf_validate(rb); - } - - /* empty the ring buffer */ - while(ringbuf_first(rb)) - ringbuf_popfirst(rb); - ringbuf_validate(rb); - count = 0; - } - } - - return 0; -} diff --git a/src/engine/e_ringbuffer.cpp b/src/engine/e_ringbuffer.cpp new file mode 100644 index 00000000..eb8a8af4 --- /dev/null +++ b/src/engine/e_ringbuffer.cpp @@ -0,0 +1,194 @@ +#include <base/system.h> + +#include "e_ringbuffer.h" + +CRingBufferBase::CItem *CRingBufferBase::NextBlock(CItem *pItem) +{ + if(pItem->m_pNext) + return pItem->m_pNext; + return m_pFirst; +} + +CRingBufferBase::CItem *CRingBufferBase::PrevBlock(CItem *pItem) +{ + if(pItem->m_pPrev) + return pItem->m_pPrev; + return m_pLast; +} + +CRingBufferBase::CItem *CRingBufferBase::MergeBack(CItem *pItem) +{ + /* make sure that this block and previous block is free */ + if(!pItem->m_Free || !pItem->m_pPrev || !pItem->m_pPrev->m_Free) + return pItem; + + /* merge the blocks */ + pItem->m_pPrev->m_Size += pItem->m_Size; + pItem->m_pPrev->m_pNext = pItem->m_pNext; + + /* fixup pointers */ + if(pItem->m_pNext) + pItem->m_pNext->m_pPrev = pItem->m_pPrev; + else + m_pLast = pItem->m_pPrev; + + if(pItem == m_pProduce) + m_pProduce = pItem->m_pPrev; + + if(pItem == m_pConsume) + m_pConsume = pItem->m_pPrev; + + /* return the current block */ + return pItem->m_pPrev; +} + +void CRingBufferBase::Init(void *pMemory, int Size, int Flags) +{ + mem_zero(pMemory, Size); + m_Size = (Size)/sizeof(CItem)*sizeof(CItem); + m_pFirst = (CItem *)pMemory; + m_pFirst->m_Free = 1; + m_pFirst->m_Size = m_Size; + m_pLast = m_pFirst; + m_pProduce = m_pFirst; + m_pConsume = m_pFirst; + m_Flags = Flags; + +} + +void *CRingBufferBase::Allocate(int Size) +{ + int WantedSize = (Size+sizeof(CItem)+sizeof(CItem)-1)/sizeof(CItem)*sizeof(CItem); + CItem *pBlock = 0; + + /* check if we even can fit this block */ + if(WantedSize > m_Size) + return 0; + + while(1) + { + /* check for space */ + if(m_pProduce->m_Free) + { + if(m_pProduce->m_Size >= WantedSize) + pBlock = m_pProduce; + else + { + /* wrap around to try to find a block */ + if(m_pFirst->m_Free && m_pFirst->m_Size >= WantedSize) + pBlock = m_pFirst; + } + } + + if(pBlock) + break; + else + { + /* we have no block, check our policy and see what todo */ + if(m_Flags&FLAG_RECYCLE) + { + if(!PopFirst()) + return 0; + } + else + return 0; + } + } + + /* okey, we have our block */ + + /* split the block if needed */ + if(pBlock->m_Size > WantedSize) + { + CItem *pNewItem = (CItem *)((char *)pBlock + WantedSize); + pNewItem->m_pPrev = pBlock; + pNewItem->m_pNext = pBlock->m_pNext; + if(pNewItem->m_pNext) + pNewItem->m_pNext->m_pPrev = pNewItem; + pBlock->m_pNext = pNewItem; + + pNewItem->m_Free = 1; + pNewItem->m_Size = pBlock->m_Size - WantedSize; + pBlock->m_Size = WantedSize; + + if(!pNewItem->m_pNext) + m_pLast = pNewItem; + } + + + /* set next block */ + m_pProduce = NextBlock(pBlock); + + /* set as used and return the item pointer */ + pBlock->m_Free = 0; + return (void *)(pBlock+1); +} + +int CRingBufferBase::PopFirst() +{ + if(m_pConsume->m_Free) + return 0; + + /* set the free flag */ + m_pConsume->m_Free = 1; + + /* previous block is also free, merge them */ + m_pConsume = MergeBack(m_pConsume); + + /* advance the consume pointer */ + m_pConsume = NextBlock(m_pConsume); + while(m_pConsume->m_Free && m_pConsume != m_pProduce) + { + m_pConsume = MergeBack(m_pConsume); + m_pConsume = NextBlock(m_pConsume); + } + + /* in the case that we have catched up with the produce pointer */ + /* we might stand on a free block so merge em */ + MergeBack(m_pConsume); + return 1; +} + + +void *CRingBufferBase::Prev(void *pCurrent) +{ + CItem *pItem = ((CItem *)pCurrent) - 1; + + while(1) + { + pItem = PrevBlock(pItem); + if(pItem == m_pProduce) + return 0; + if(!pItem->m_Free) + return pItem+1; + } +} + +void *CRingBufferBase::Next(void *pCurrent) +{ + CItem *pItem = ((CItem *)pCurrent) - 1; + + while(1) + { + pItem = NextBlock(pItem); + if(pItem == m_pProduce) + return 0; + if(!pItem->m_Free) + return pItem+1; + } +} + +void *CRingBufferBase::First() +{ + if(m_pConsume->m_Free) + return 0; + return (void *)(m_pConsume+1); +} + +void *CRingBufferBase::Last() +{ + if(!m_pProduce->m_Free) + return m_pProduce+1; + return Prev(m_pProduce+1); +} + diff --git a/src/engine/e_ringbuffer.h b/src/engine/e_ringbuffer.h index 6113c19b..b9f7219c 100644 --- a/src/engine/e_ringbuffer.h +++ b/src/engine/e_ringbuffer.h @@ -3,21 +3,64 @@ typedef struct RINGBUFFER RINGBUFFER; -enum +class CRingBufferBase { - /* Will start to destroy items to try to fit the next one */ - RINGBUF_FLAG_RECYCLE=1 + class CItem + { + public: + CItem *m_pPrev; + CItem *m_pNext; + int m_Free; + int m_Size; + }; + + CItem *m_pProduce; + CItem *m_pConsume; + + CItem *m_pFirst; + CItem *m_pLast; + + void *m_pMemory; + int m_Size; + int m_Flags; + + CItem *NextBlock(CItem *pItem); + CItem *PrevBlock(CItem *pItem); + CItem *MergeBack(CItem *pItem); +protected: + void *Allocate(int Size); + + void *Prev(void *pCurrent); + void *Next(void *pCurrent); + void *First(); + void *Last(); + + void Init(void *pMemory, int Size, int Flags); + int PopFirst(); +public: + enum + { + /* Will start to destroy items to try to fit the next one */ + FLAG_RECYCLE=1 + }; }; - -RINGBUFFER *ringbuf_init(void *memory, int size, int flags); -void ringbuf_clear(RINGBUFFER *rb); -void *ringbuf_allocate(RINGBUFFER *rb, int size); -void *ringbuf_prev(RINGBUFFER *rb, void *current); -void *ringbuf_next(RINGBUFFER *rb, void *current); -void *ringbuf_first(RINGBUFFER *rb); -void *ringbuf_last(RINGBUFFER *rb); +template<typename T, int TSIZE, int TFLAGS=0> +class TStaticRingBuffer : public CRingBufferBase +{ + unsigned char m_aBuffer[TSIZE]; +public: + TStaticRingBuffer() { Init(); } + + void Init() { CRingBufferBase::Init(m_aBuffer, TSIZE, TFLAGS); } + + T *Allocate(int Size) { return (T*)CRingBufferBase::Allocate(Size); } + int PopFirst() { return CRingBufferBase::PopFirst(); } -int ringbuf_popfirst(RINGBUFFER *rb); + T *Prev(T *pCurrent) { return (T*)CRingBufferBase::Prev(pCurrent); } + T *Next(T *pCurrent) { return (T*)CRingBufferBase::Next(pCurrent); } + T *First() { return (T*)CRingBufferBase::First(); } + T *Last() { return (T*)CRingBufferBase::Last(); } +}; #endif diff --git a/src/engine/e_server_interface.h b/src/engine/e_server_interface.h index 5b1e6327..c325b9a1 100644 --- a/src/engine/e_server_interface.h +++ b/src/engine/e_server_interface.h @@ -2,10 +2,6 @@ #ifndef ENGINE_SERVER_INTERFACE_H #define ENGINE_SERVER_INTERFACE_H -#ifdef __cplusplus -extern "C" { -#endif - #include "e_if_other.h" #include "e_if_server.h" #include "e_if_msg.h" @@ -13,8 +9,4 @@ extern "C" { #include "e_console.h" /* TODO: clean this up*/ -#ifdef __cplusplus -} -#endif - #endif diff --git a/src/engine/e_snapshot.c b/src/engine/e_snapshot.c deleted file mode 100644 index cce8a06e..00000000 --- a/src/engine/e_snapshot.c +++ /dev/null @@ -1,604 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdlib.h> -#include "e_snapshot.h" -#include "e_engine.h" -#include "e_compression.h" -#include "e_common_interface.h" - - -/* TODO: strange arbitrary number */ -static short item_sizes[64] = {0}; - -void snap_set_staticsize(int itemtype, int size) -{ - item_sizes[itemtype] = size; -} - -int *snapitem_data(SNAPSHOT_ITEM *item) { return (int *)(item+1); } -int snapitem_type(SNAPSHOT_ITEM *item) { return item->type_and_id>>16; } -int snapitem_id(SNAPSHOT_ITEM *item) { return item->type_and_id&0xffff; } -int snapitem_key(SNAPSHOT_ITEM *item) { return item->type_and_id; } - -int *snapshot_offsets(SNAPSHOT *snap) { return (int *)(snap+1); } -char *snapshot_datastart(SNAPSHOT *snap) { return (char*)(snapshot_offsets(snap)+snap->num_items); } - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index) -{ return (SNAPSHOT_ITEM *)(snapshot_datastart(snap) + snapshot_offsets(snap)[index]); } - -int snapshot_get_item_datasize(SNAPSHOT *snap, int index) -{ - if(index == snap->num_items-1) - return (snap->data_size - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); - return (snapshot_offsets(snap)[index+1] - snapshot_offsets(snap)[index]) - sizeof(SNAPSHOT_ITEM); -} - -int snapshot_get_item_index(SNAPSHOT *snap, int key) -{ - /* TODO: OPT: this should not be a linear search. very bad */ - int i; - for(i = 0; i < snap->num_items; i++) - { - if(snapitem_key(snapshot_get_item(snap, i)) == key) - return i; - } - return -1; -} -typedef struct -{ - int num; - int keys[64]; - int index[64]; -} ITEMLIST; -static ITEMLIST sorted[256]; - -static int snapshot_generate_hash(SNAPSHOT *snap) -{ - int i, key, hashid; - - for(i = 0; i < 256; i++) - sorted[i].num = 0; - - for(i = 0; i < snap->num_items; i++) - { - key = snapitem_key(snapshot_get_item(snap, i)); - hashid = ((key>>8)&0xf0) | (key&0xf); - if(sorted[hashid].num != 64) - { - sorted[hashid].index[sorted[hashid].num] = i; - sorted[hashid].keys[sorted[hashid].num] = key; - sorted[hashid].num++; - } - } - return 0; -} - -int snapshot_get_item_index_hashed(SNAPSHOT *snap, int key) -{ - int hashid = ((key>>8)&0xf0) | (key&0xf); - int i; - for(i = 0; i < sorted[hashid].num; i++) - { - if(sorted[hashid].keys[i] == key) - return sorted[hashid].index[i]; - } - - return -1; -} - -typedef struct -{ - int num_deleted_items; - int num_update_items; - int num_temp_items; /* needed? */ - int data[1]; - - /* - char *data_start() { return (char *)&offsets[num_deleted_items+num_update_items+num_temp_items]; } - - int deleted_item(int index) { return offsets[index]; } - item *update_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+index]); } - item *temp_item(int index) { return (item *)(data_start() + offsets[num_deleted_items+num_update_items+index]); } - */ -} SNAPSHOT_DELTA; - - -static const int MAX_ITEMS = 512; -static SNAPSHOT_DELTA empty = {0,0,0,{0}}; - -void *snapshot_empty_delta() -{ - return ∅ -} - -int snapshot_crc(SNAPSHOT *snap) -{ - int crc = 0; - int i, b; - SNAPSHOT_ITEM *item; - int size; - - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - - for(b = 0; b < size/4; b++) - crc += snapitem_data(item)[b]; - } - return crc; -} - -void snapshot_debug_dump(SNAPSHOT *snap) -{ - int size, i, b; - SNAPSHOT_ITEM *item; - - dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items); - for(i = 0; i < snap->num_items; i++) - { - item = snapshot_get_item(snap, i); - size = snapshot_get_item_datasize(snap, i); - dbg_msg("snapshot", "\ttype=%d id=%d", snapitem_type(item), snapitem_id(item)); - for(b = 0; b < size/4; b++) - dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, snapitem_data(item)[b], snapitem_data(item)[b]); - } -} - -static int diff_item(int *past, int *current, int *out, int size) -{ - int needed = 0; - while(size) - { - *out = *current-*past; - needed |= *out; - out++; - past++; - current++; - size--; - } - - return needed; -} - -int snapshot_data_rate[0xffff] = {0}; -int snapshot_data_updates[0xffff] = {0}; -static int snapshot_current = 0; - -static void undiff_item(int *past, int *diff, int *out, int size) -{ - while(size) - { - *out = *past+*diff; - - if(*diff == 0) - snapshot_data_rate[snapshot_current] += 1; - else - { - unsigned char buf[16]; - unsigned char *end = vint_pack(buf, *diff); - snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; - } - - out++; - past++; - diff++; - size--; - } -} - - -/* TODO: OPT: this should be made much faster */ -int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata) -{ - static PERFORMACE_INFO hash_scope = {"hash", 0}; - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata; - int *data = (int *)delta->data; - int i, itemsize, pastindex; - SNAPSHOT_ITEM *fromitem; - SNAPSHOT_ITEM *curitem; - SNAPSHOT_ITEM *pastitem; - int count = 0; - int size_count = 0; - - delta->num_deleted_items = 0; - delta->num_update_items = 0; - delta->num_temp_items = 0; - - perf_start(&hash_scope); - snapshot_generate_hash(to); - perf_end(); - - /* pack deleted stuff */ - { - static PERFORMACE_INFO scope = {"delete", 0}; - perf_start(&scope); - - for(i = 0; i < from->num_items; i++) - { - fromitem = snapshot_get_item(from, i); - if(snapshot_get_item_index_hashed(to, (snapitem_key(fromitem))) == -1) - { - /* deleted */ - delta->num_deleted_items++; - *data = snapitem_key(fromitem); - data++; - } - } - - perf_end(); - } - - perf_start(&hash_scope); - snapshot_generate_hash(from); - perf_end(); - - /* pack updated stuff */ - { - static PERFORMACE_INFO scope = {"update", 0}; - int pastindecies[1024]; - perf_start(&scope); - - /* fetch previous indices */ - /* we do this as a separate pass because it helps the cache */ - { - static PERFORMACE_INFO scope = {"find", 0}; - perf_start(&scope); - for(i = 0; i < to->num_items; i++) - { - curitem = snapshot_get_item(to, i); /* O(1) .. O(n) */ - pastindecies[i] = snapshot_get_item_index_hashed(from, snapitem_key(curitem)); /* O(n) .. O(n^n)*/ - } - perf_end(); - } - - for(i = 0; i < to->num_items; i++) - { - /* do delta */ - itemsize = snapshot_get_item_datasize(to, i); /* O(1) .. O(n) */ - curitem = snapshot_get_item(to, i); /* O(1) .. O(n) */ - pastindex = pastindecies[i]; - - if(pastindex != -1) - { - static PERFORMACE_INFO scope = {"diff", 0}; - int *item_data_dst = data+3; - perf_start(&scope); - - pastitem = snapshot_get_item(from, pastindex); - - if(item_sizes[snapitem_type(curitem)]) - item_data_dst = data+2; - - if(diff_item((int*)snapitem_data(pastitem), (int*)snapitem_data(curitem), item_data_dst, itemsize/4)) - { - - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - if(!item_sizes[snapitem_type(curitem)]) - *data++ = itemsize/4; - data += itemsize/4; - delta->num_update_items++; - } - perf_end(); - } - else - { - static PERFORMACE_INFO scope = {"copy", 0}; - perf_start(&scope); - - *data++ = snapitem_type(curitem); - *data++ = snapitem_id(curitem); - if(!item_sizes[snapitem_type(curitem)]) - *data++ = itemsize/4; - - mem_copy(data, snapitem_data(curitem), itemsize); - size_count += itemsize; - data += itemsize/4; - delta->num_update_items++; - count++; - - perf_end(); - } - } - - perf_end(); - } - - if(0) - { - dbg_msg("snapshot", "%d %d %d", - delta->num_deleted_items, - delta->num_update_items, - delta->num_temp_items); - } - - /* - // TODO: pack temp stuff - - // finish - //mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int)); - //mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int)); - //mem_copy(delta->data_start(), data, data_size); - //delta->data_size = data_size; - * */ - - if(!delta->num_deleted_items && !delta->num_update_items && !delta->num_temp_items) - return 0; - - return (int)((char*)data-(char*)dstdata); -} - -static int range_check(void *end, void *ptr, int size) -{ - if((const char *)ptr + size > (const char *)end) - return -1; - return 0; -} - -int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *srcdata, int data_size) -{ - SNAPBUILD builder; - SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)srcdata; - int *data = (int *)delta->data; - int *end = (int *)(((char *)srcdata + data_size)); - - SNAPSHOT_ITEM *fromitem; - int i, d, keep, itemsize; - int *deleted; - int id, type, key; - int fromindex; - int *newdata; - - snapbuild_init(&builder); - - /* unpack deleted stuff */ - deleted = data; - data += delta->num_deleted_items; - if(data > end) - return -1; - - /* copy all non deleted stuff */ - for(i = 0; i < from->num_items; i++) - { - /* dbg_assert(0, "fail!"); */ - fromitem = snapshot_get_item(from, i); - itemsize = snapshot_get_item_datasize(from, i); - keep = 1; - for(d = 0; d < delta->num_deleted_items; d++) - { - if(deleted[d] == snapitem_key(fromitem)) - { - keep = 0; - break; - } - } - - if(keep) - { - /* keep it */ - mem_copy( - snapbuild_new_item(&builder, snapitem_type(fromitem), snapitem_id(fromitem), itemsize), - snapitem_data(fromitem), itemsize); - } - } - - /* unpack updated stuff */ - for(i = 0; i < delta->num_update_items; i++) - { - if(data+2 > end) - return -1; - - type = *data++; - id = *data++; - if(item_sizes[type]) - itemsize = item_sizes[type]; - else - { - if(data+1 > end) - return -2; - itemsize = (*data++) * 4; - } - snapshot_current = type; - - if(range_check(end, data, itemsize) || itemsize < 0) return -3; - - key = (type<<16)|id; - - /* create the item if needed */ - newdata = snapbuild_get_item_data(&builder, key); - if(!newdata) - newdata = (int *)snapbuild_new_item(&builder, key>>16, key&0xffff, itemsize); - - /*if(range_check(end, newdata, itemsize)) return -4;*/ - - fromindex = snapshot_get_item_index(from, key); - if(fromindex != -1) - { - /* we got an update so we need to apply the diff */ - undiff_item((int *)snapitem_data(snapshot_get_item(from, fromindex)), data, newdata, itemsize/4); - snapshot_data_updates[snapshot_current]++; - } - else /* no previous, just copy the data */ - { - mem_copy(newdata, data, itemsize); - snapshot_data_rate[snapshot_current] += itemsize*8; - snapshot_data_updates[snapshot_current]++; - } - - data += itemsize/4; - } - - /* finish up */ - return snapbuild_finish(&builder, to); -} - -/* SNAPSTORAGE */ - -void snapstorage_init(SNAPSTORAGE *ss) -{ - ss->first = 0; -} - -void snapstorage_purge_all(SNAPSTORAGE *ss) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - SNAPSTORAGE_HOLDER *next; - - while(h) - { - next = h->next; - mem_free(h); - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick) -{ - SNAPSTORAGE_HOLDER *next; - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - next = h->next; - if(h->tick >= tick) - return; /* no more to remove */ - mem_free(h); - - /* did we come to the end of the list? */ - if (!next) - break; - - ss->first = next; - next->prev = 0x0; - - h = next; - } - - /* no more snapshots in storage */ - ss->first = 0; - ss->last = 0; -} - -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt) -{ - /* allocate memory for holder + snapshot_data */ - SNAPSTORAGE_HOLDER *h; - int total_size = sizeof(SNAPSTORAGE_HOLDER)+data_size; - - if(create_alt) - total_size += data_size; - - h = (SNAPSTORAGE_HOLDER *)mem_alloc(total_size, 1); - - /* set data */ - h->tick = tick; - h->tagtime = tagtime; - h->snap_size = data_size; - h->snap = (SNAPSHOT*)(h+1); - mem_copy(h->snap, data, data_size); - - if(create_alt) /* create alternative if wanted */ - { - h->alt_snap = (SNAPSHOT*)(((char *)h->snap) + data_size); - mem_copy(h->alt_snap, data, data_size); - } - else - h->alt_snap = 0; - - - /* link */ - h->next = 0; - h->prev = ss->last; - if(ss->last) - ss->last->next = h; - else - ss->first = h; - ss->last = h; -} - -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data) -{ - SNAPSTORAGE_HOLDER *h = ss->first; - - while(h) - { - if(h->tick == tick) - { - if(tagtime) - *tagtime = h->tagtime; - if(data) - *data = h->snap; - if(alt_data) - *alt_data = h->alt_snap; - return h->snap_size; - } - - h = h->next; - } - - return -1; -} - -/* SNAPBUILD */ - -void snapbuild_init(SNAPBUILD *sb) -{ - sb->data_size = 0; - sb->num_items = 0; -} - -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index) -{ - return (SNAPSHOT_ITEM *)&(sb->data[sb->offsets[index]]); -} - -int *snapbuild_get_item_data(SNAPBUILD *sb, int key) -{ - int i; - for(i = 0; i < sb->num_items; i++) - { - if(snapitem_key(snapbuild_get_item(sb, i)) == key) - return (int *)snapitem_data(snapbuild_get_item(sb, i)); - } - return 0; -} - -int snapbuild_finish(SNAPBUILD *sb, void *snapdata) -{ - /* flattern and make the snapshot */ - SNAPSHOT *snap = (SNAPSHOT *)snapdata; - int offset_size = sizeof(int)*sb->num_items; - snap->data_size = sb->data_size; - snap->num_items = sb->num_items; - mem_copy(snapshot_offsets(snap), sb->offsets, offset_size); - mem_copy(snapshot_datastart(snap), sb->data, sb->data_size); - return sizeof(SNAPSHOT) + offset_size + sb->data_size; -} - -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size) -{ - SNAPSHOT_ITEM *obj = (SNAPSHOT_ITEM *)(sb->data+sb->data_size); - - /*if(stress_prob(0.01f)) - { - size += ((rand()%5) - 2)*4; - if(size < 0) - size = 0; - }*/ - - mem_zero(obj, sizeof(SNAPSHOT_ITEM) + size); - obj->type_and_id = (type<<16)|id; - sb->offsets[sb->num_items] = sb->data_size; - sb->data_size += sizeof(SNAPSHOT_ITEM) + size; - sb->num_items++; - - dbg_assert(sb->data_size < MAX_SNAPSHOT_SIZE, "too much data"); - dbg_assert(sb->num_items < SNAPBUILD_MAX_ITEMS, "too many items"); - - return snapitem_data(obj); -} diff --git a/src/engine/e_snapshot.cpp b/src/engine/e_snapshot.cpp new file mode 100644 index 00000000..65487665 --- /dev/null +++ b/src/engine/e_snapshot.cpp @@ -0,0 +1,571 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include <stdlib.h> +#include "e_snapshot.h" +#include "e_engine.h" +#include "e_compression.h" +#include "e_common_interface.h" + + +/* TODO: strange arbitrary number */ +static short item_sizes[64] = {0}; + +void snap_set_staticsize(int itemtype, int size) +{ + item_sizes[itemtype] = size; +} + +CSnapshotItem *CSnapshot::GetItem(int Index) +{ + return (CSnapshotItem *)(DataStart() + Offsets()[Index]); +} + +int CSnapshot::GetItemSize(int Index) +{ + if(Index == m_NumItems-1) + return (m_DataSize - Offsets()[Index]) - sizeof(CSnapshotItem); + return (Offsets()[Index+1] - Offsets()[Index]) - sizeof(CSnapshotItem); +} + +int CSnapshot::GetItemIndex(int Key) +{ + /* TODO: OPT: this should not be a linear search. very bad */ + for(int i = 0; i < m_NumItems; i++) + { + if(GetItem(i)->Key() == Key) + return i; + } + return -1; +} + +// TODO: clean up this +typedef struct +{ + int num; + int keys[64]; + int index[64]; +} ITEMLIST; + +static ITEMLIST sorted[256]; + +int CSnapshot::GenerateHash() +{ + for(int i = 0; i < 256; i++) + sorted[i].num = 0; + + for(int i = 0; i < m_NumItems; i++) + { + int Key = GetItem(i)->Key(); + int HashID = ((Key>>8)&0xf0) | (Key&0xf); + if(sorted[HashID].num != 64) + { + sorted[HashID].index[sorted[HashID].num] = i; + sorted[HashID].keys[sorted[HashID].num] = Key; + sorted[HashID].num++; + } + } + return 0; +} + +int CSnapshot::GetItemIndexHashed(int Key) +{ + int HashID = ((Key>>8)&0xf0) | (Key&0xf); + for(int i = 0; i < sorted[HashID].num; i++) + { + if(sorted[HashID].keys[i] == Key) + return sorted[HashID].index[i]; + } + + return -1; +} + + + +static const int MAX_ITEMS = 512; +static CSnapshotDelta empty = {0,0,0,{0}}; + +CSnapshotDelta *CSnapshot::EmptyDelta() +{ + return ∅ +} + +int CSnapshot::Crc() +{ + int crc = 0; + + for(int i = 0; i < m_NumItems; i++) + { + CSnapshotItem *pItem = GetItem(i); + int Size = GetItemSize(i); + + for(int b = 0; b < Size/4; b++) + crc += pItem->Data()[b]; + } + return crc; +} + +void CSnapshot::DebugDump() +{ + dbg_msg("snapshot", "data_size=%d num_items=%d", m_DataSize, m_NumItems); + for(int i = 0; i < m_NumItems; i++) + { + CSnapshotItem *pItem = GetItem(i); + int Size = GetItemSize(i); + dbg_msg("snapshot", "\ttype=%d id=%d", pItem->Type(), pItem->ID()); + for(int b = 0; b < Size/4; b++) + dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, pItem->Data()[b], pItem->Data()[b]); + } +} + + +// TODO: remove these +int snapshot_data_rate[0xffff] = {0}; +int snapshot_data_updates[0xffff] = {0}; +static int snapshot_current = 0; + +static int diff_item(int *past, int *current, int *out, int size) +{ + int needed = 0; + while(size) + { + *out = *current-*past; + needed |= *out; + out++; + past++; + current++; + size--; + } + + return needed; +} + +static void undiff_item(int *past, int *diff, int *out, int size) +{ + while(size) + { + *out = *past+*diff; + + if(*diff == 0) + snapshot_data_rate[snapshot_current] += 1; + else + { + unsigned char buf[16]; + unsigned char *end = vint_pack(buf, *diff); + snapshot_data_rate[snapshot_current] += (int)(end - (unsigned char*)buf) * 8; + } + + out++; + past++; + diff++; + size--; + } +} + + +/* TODO: OPT: this should be made much faster */ +int CSnapshot::CreateDelta(CSnapshot *from, CSnapshot *to, void *dstdata) +{ + static PERFORMACE_INFO hash_scope = {"hash", 0}; + CSnapshotDelta *delta = (CSnapshotDelta *)dstdata; + int *data = (int *)delta->m_pData; + int i, itemsize, pastindex; + CSnapshotItem *pFromItem; + CSnapshotItem *pCurItem; + CSnapshotItem *pPastItem; + int count = 0; + int size_count = 0; + + delta->m_NumDeletedItems = 0; + delta->m_NumUpdateItems = 0; + delta->m_NumTempItems = 0; + + perf_start(&hash_scope); + to->GenerateHash(); + perf_end(); + + /* pack deleted stuff */ + { + static PERFORMACE_INFO scope = {"delete", 0}; + perf_start(&scope); + + for(i = 0; i < from->m_NumItems; i++) + { + pFromItem = from->GetItem(i); + if(to->GetItemIndexHashed(pFromItem->Key()) == -1) + { + /* deleted */ + delta->m_NumDeletedItems++; + *data = pFromItem->Key(); + data++; + } + } + + perf_end(); + } + + perf_start(&hash_scope); + from->GenerateHash(); + perf_end(); + + /* pack updated stuff */ + { + static PERFORMACE_INFO scope = {"update", 0}; + int pastindecies[1024]; + perf_start(&scope); + + /* fetch previous indices */ + /* we do this as a separate pass because it helps the cache */ + { + static PERFORMACE_INFO scope = {"find", 0}; + perf_start(&scope); + for(i = 0; i < to->m_NumItems; i++) + { + pCurItem = to->GetItem(i); /* O(1) .. O(n) */ + pastindecies[i] = from->GetItemIndexHashed(pCurItem->Key()); /* O(n) .. O(n^n)*/ + } + perf_end(); + } + + for(i = 0; i < to->m_NumItems; i++) + { + /* do delta */ + itemsize = to->GetItemSize(i); /* O(1) .. O(n) */ + pCurItem = to->GetItem(i); /* O(1) .. O(n) */ + pastindex = pastindecies[i]; + + if(pastindex != -1) + { + static PERFORMACE_INFO scope = {"diff", 0}; + int *item_data_dst = data+3; + perf_start(&scope); + + pPastItem = from->GetItem(pastindex); + + if(item_sizes[pCurItem->Type()]) + item_data_dst = data+2; + + if(diff_item((int*)pPastItem->Data(), (int*)pCurItem->Data(), item_data_dst, itemsize/4)) + { + + *data++ = pCurItem->Type(); + *data++ = pCurItem->ID(); + if(!item_sizes[pCurItem->Type()]) + *data++ = itemsize/4; + data += itemsize/4; + delta->m_NumUpdateItems++; + } + perf_end(); + } + else + { + static PERFORMACE_INFO scope = {"copy", 0}; + perf_start(&scope); + + *data++ = pCurItem->Type(); + *data++ = pCurItem->ID(); + if(!item_sizes[pCurItem->Type()]) + *data++ = itemsize/4; + + mem_copy(data, pCurItem->Data(), itemsize); + size_count += itemsize; + data += itemsize/4; + delta->m_NumUpdateItems++; + count++; + + perf_end(); + } + } + + perf_end(); + } + + if(0) + { + dbg_msg("snapshot", "%d %d %d", + delta->m_NumDeletedItems, + delta->m_NumUpdateItems, + delta->m_NumTempItems); + } + + /* + // TODO: pack temp stuff + + // finish + //mem_copy(delta->offsets, deleted, delta->num_deleted_items*sizeof(int)); + //mem_copy(&(delta->offsets[delta->num_deleted_items]), update, delta->num_update_items*sizeof(int)); + //mem_copy(&(delta->offsets[delta->num_deleted_items+delta->num_update_items]), temp, delta->num_temp_items*sizeof(int)); + //mem_copy(delta->data_start(), data, data_size); + //delta->data_size = data_size; + * */ + + if(!delta->m_NumDeletedItems && !delta->m_NumUpdateItems && !delta->m_NumTempItems) + return 0; + + return (int)((char*)data-(char*)dstdata); +} + +static int range_check(void *end, void *ptr, int size) +{ + if((const char *)ptr + size > (const char *)end) + return -1; + return 0; +} + +int CSnapshot::UnpackDelta(CSnapshot *from, CSnapshot *to, void *srcdata, int data_size) +{ + CSnapshotBuilder builder; + CSnapshotDelta *delta = (CSnapshotDelta *)srcdata; + int *data = (int *)delta->m_pData; + int *end = (int *)(((char *)srcdata + data_size)); + + CSnapshotItem *fromitem; + int i, d, keep, itemsize; + int *deleted; + int id, type, key; + int fromindex; + int *newdata; + + builder.Init(); + + /* unpack deleted stuff */ + deleted = data; + data += delta->m_NumDeletedItems; + if(data > end) + return -1; + + /* copy all non deleted stuff */ + for(i = 0; i < from->m_NumItems; i++) + { + /* dbg_assert(0, "fail!"); */ + fromitem = from->GetItem(i); + itemsize = from->GetItemSize(i); + keep = 1; + for(d = 0; d < delta->m_NumDeletedItems; d++) + { + if(deleted[d] == fromitem->Key()) + { + keep = 0; + break; + } + } + + if(keep) + { + /* keep it */ + mem_copy( + builder.NewItem(fromitem->Type(), fromitem->ID(), itemsize), + fromitem->Data(), itemsize); + } + } + + /* unpack updated stuff */ + for(i = 0; i < delta->m_NumUpdateItems; i++) + { + if(data+2 > end) + return -1; + + type = *data++; + id = *data++; + if(item_sizes[type]) + itemsize = item_sizes[type]; + else + { + if(data+1 > end) + return -2; + itemsize = (*data++) * 4; + } + snapshot_current = type; + + if(range_check(end, data, itemsize) || itemsize < 0) return -3; + + key = (type<<16)|id; + + /* create the item if needed */ + newdata = builder.GetItemData(key); + if(!newdata) + newdata = (int *)builder.NewItem(key>>16, key&0xffff, itemsize); + + /*if(range_check(end, newdata, itemsize)) return -4;*/ + + fromindex = from->GetItemIndex(key); + if(fromindex != -1) + { + /* we got an update so we need to apply the diff */ + undiff_item((int *)from->GetItem(fromindex)->Data(), data, newdata, itemsize/4); + snapshot_data_updates[snapshot_current]++; + } + else /* no previous, just copy the data */ + { + mem_copy(newdata, data, itemsize); + snapshot_data_rate[snapshot_current] += itemsize*8; + snapshot_data_updates[snapshot_current]++; + } + + data += itemsize/4; + } + + /* finish up */ + return builder.Finish(to); +} + +/* CSnapshotStorage */ + +void CSnapshotStorage::Init() +{ + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::PurgeAll() +{ + CHolder *pHolder = m_pFirst; + CHolder *pNext; + + while(pHolder) + { + pNext = pHolder->m_pNext; + mem_free(pHolder); + pHolder = pNext; + } + + /* no more snapshots in storage */ + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::PurgeUntil(int Tick) +{ + CHolder *pHolder = m_pFirst; + CHolder *pNext; + + while(pHolder) + { + pNext = pHolder->m_pNext; + if(pHolder->m_Tick >= Tick) + return; /* no more to remove */ + mem_free(pHolder); + + /* did we come to the end of the list? */ + if (!pNext) + break; + + m_pFirst = pNext; + pNext->m_pPrev = 0x0; + + pHolder = pNext; + } + + /* no more snapshots in storage */ + m_pFirst = 0; + m_pLast = 0; +} + +void CSnapshotStorage::Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt) +{ + /* allocate memory for holder + snapshot_data */ + int TotalSize = sizeof(CHolder)+DataSize; + + if(CreateAlt) + TotalSize += DataSize; + + CHolder *pHolder = (CHolder *)mem_alloc(TotalSize, 1); + + /* set data */ + pHolder->m_Tick = Tick; + pHolder->m_Tagtime = Tagtime; + pHolder->m_SnapSize = DataSize; + pHolder->m_pSnap = (CSnapshot*)(pHolder+1); + mem_copy(pHolder->m_pSnap, pData, DataSize); + + if(CreateAlt) /* create alternative if wanted */ + { + pHolder->m_pAltSnap = (CSnapshot*)(((char *)pHolder->m_pSnap) + DataSize); + mem_copy(pHolder->m_pAltSnap, pData, DataSize); + } + else + pHolder->m_pAltSnap = 0; + + + /* link */ + pHolder->m_pNext = 0; + pHolder->m_pPrev = m_pLast; + if(m_pLast) + m_pLast->m_pNext = pHolder; + else + m_pFirst = pHolder; + m_pLast = pHolder; +} + +int CSnapshotStorage::Get(int Tick, int64 *pTagtime, CSnapshot **ppData, CSnapshot **ppAltData) +{ + CHolder *pHolder = m_pFirst; + + while(pHolder) + { + if(pHolder->m_Tick == Tick) + { + if(pTagtime) + *pTagtime = pHolder->m_Tagtime; + if(ppData) + *ppData = pHolder->m_pSnap; + if(ppAltData) + *ppData = pHolder->m_pAltSnap; + return pHolder->m_SnapSize; + } + + pHolder = pHolder->m_pNext; + } + + return -1; +} + +/* CSnapshotBuilder */ + +void CSnapshotBuilder::Init() +{ + m_DataSize = 0; + m_NumItems = 0; +} + +CSnapshotItem *CSnapshotBuilder::GetItem(int Index) +{ + return (CSnapshotItem *)&(m_aData[m_aOffsets[Index]]); +} + +int *CSnapshotBuilder::GetItemData(int key) +{ + int i; + for(i = 0; i < m_NumItems; i++) + { + if(GetItem(i)->Key() == key) + return (int *)GetItem(i)->Data(); + } + return 0; +} + +int CSnapshotBuilder::Finish(void *snapdata) +{ + /* flattern and make the snapshot */ + CSnapshot *pSnap = (CSnapshot *)snapdata; + int OffsetSize = sizeof(int)*m_NumItems; + pSnap->m_DataSize = m_DataSize; + pSnap->m_NumItems = m_NumItems; + mem_copy(pSnap->Offsets(), m_aOffsets, OffsetSize); + mem_copy(pSnap->DataStart(), m_aData, m_DataSize); + return sizeof(CSnapshot) + OffsetSize + m_DataSize; +} + +void *CSnapshotBuilder::NewItem(int Type, int ID, int Size) +{ + CSnapshotItem *pObj = (CSnapshotItem *)(m_aData + m_DataSize); + + mem_zero(pObj, sizeof(CSnapshotItem) + Size); + pObj->m_TypeAndID = (Type<<16)|ID; + m_aOffsets[m_NumItems] = m_DataSize; + m_DataSize += sizeof(CSnapshotItem) + Size; + m_NumItems++; + + dbg_assert(m_DataSize < CSnapshot::MAX_SIZE, "too much data"); + dbg_assert(m_NumItems < MAX_ITEMS, "too many items"); + + return pObj->Data(); +} diff --git a/src/engine/e_snapshot.h b/src/engine/e_snapshot.h index f57c95c0..60dc254c 100644 --- a/src/engine/e_snapshot.h +++ b/src/engine/e_snapshot.h @@ -4,89 +4,115 @@ #include <base/system.h> -/* SNAPSHOT */ +/* CSnapshot */ -enum + + +class CSnapshotItem { - MAX_SNAPSHOT_SIZE=64*1024 +public: + int m_TypeAndID; + + int *Data() { return (int *)(this+1); } + int Type() { return m_TypeAndID>>16; } + int ID() { return m_TypeAndID&0xffff; } + int Key() { return m_TypeAndID; } }; -typedef struct +class CSnapshotDelta { - int type_and_id; -} SNAPSHOT_ITEM; +public: + int m_NumDeletedItems; + int m_NumUpdateItems; + int m_NumTempItems; /* needed? */ + int m_pData[1]; +}; -typedef struct +// TODO: hide a lot of these members +class CSnapshot { - int data_size; - int num_items; -} SNAPSHOT; - -int *snapitem_data(SNAPSHOT_ITEM *item); -int snapitem_type(SNAPSHOT_ITEM *item); -int snapitem_id(SNAPSHOT_ITEM *item); -int snapitem_key(SNAPSHOT_ITEM *item); - -int *snapshot_offsets(SNAPSHOT *snap); -char *snapshot_datastart(SNAPSHOT *snap); - -SNAPSHOT_ITEM *snapshot_get_item(SNAPSHOT *snap, int index); -int snapshot_get_item_datasize(SNAPSHOT *snap, int index); -int snapshot_get_item_index(SNAPSHOT *snap, int key); +public: + enum + { + MAX_SIZE=64*1024 + }; + + int m_DataSize; + int m_NumItems; + + int *Offsets() const { return (int *)(this+1); } + char *DataStart() const { return (char*)(Offsets()+m_NumItems); } + CSnapshotItem *GetItem(int Index); + int GetItemSize(int Index); + int GetItemIndex(int Key); + + int Crc(); + void DebugDump(); + + // TODO: move these + int GetItemIndexHashed(int Key); + int GenerateHash(); + + // + static CSnapshotDelta *EmptyDelta(); + static int CreateDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData); + static int UnpackDelta(CSnapshot *pFrom, CSnapshot *pTo, void *pData, int DataSize); +}; -void *snapshot_empty_delta(); -int snapshot_crc(SNAPSHOT *snap); -void snapshot_debug_dump(SNAPSHOT *snap); -int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *data); -int snapshot_unpack_delta(SNAPSHOT *from, SNAPSHOT *to, void *data, int data_size); +/* CSnapshotStorage */ -/* SNAPSTORAGE */ -typedef struct SNAPSTORAGE_HOLDER_t +class CSnapshotStorage { - struct SNAPSTORAGE_HOLDER_t *prev; - struct SNAPSTORAGE_HOLDER_t *next; - - int64 tagtime; - int tick; - - int snap_size; - SNAPSHOT *snap; - SNAPSHOT *alt_snap; -} SNAPSTORAGE_HOLDER; - -typedef struct SNAPSTORAGE_t +public: + class CHolder + { + public: + CHolder *m_pPrev; + CHolder *m_pNext; + + int64 m_Tagtime; + int m_Tick; + + int m_SnapSize; + CSnapshot *m_pSnap; + CSnapshot *m_pAltSnap; + }; + + + CHolder *m_pFirst; + CHolder *m_pLast; + + void Init(); + void PurgeAll(); + void PurgeUntil(int Tick); + void Add(int Tick, int64 Tagtime, int DataSize, void *pData, int CreateAlt); + int Get(int Tick, int64 *Tagtime, CSnapshot **pData, CSnapshot **ppAltData); +}; + +class CSnapshotBuilder { - SNAPSTORAGE_HOLDER *first; - SNAPSTORAGE_HOLDER *last; -} SNAPSTORAGE; + enum + { + MAX_ITEMS = 1024*2 + }; -void snapstorage_init(SNAPSTORAGE *ss); -void snapstorage_purge_all(SNAPSTORAGE *ss); -void snapstorage_purge_until(SNAPSTORAGE *ss, int tick); -void snapstorage_add(SNAPSTORAGE *ss, int tick, int64 tagtime, int data_size, void *data, int create_alt); -int snapstorage_get(SNAPSTORAGE *ss, int tick, int64 *tagtime, SNAPSHOT **data, SNAPSHOT **alt_data); + char m_aData[CSnapshot::MAX_SIZE]; + int m_DataSize; -/* SNAPBUILD */ + int m_aOffsets[MAX_ITEMS]; + int m_NumItems; -enum -{ - SNAPBUILD_MAX_ITEMS = 1024*2 +public: + void Init(); + + void *NewItem(int Type, int ID, int Size); + + CSnapshotItem *GetItem(int Index); + int *GetItemData(int Key); + + int Finish(void *Snapdata); }; -typedef struct SNAPBUILD -{ - char data[MAX_SNAPSHOT_SIZE]; - int data_size; - - int offsets[SNAPBUILD_MAX_ITEMS]; - int num_items; -} SNAPBUILD; - -void snapbuild_init(SNAPBUILD *sb); -SNAPSHOT_ITEM *snapbuild_get_item(SNAPBUILD *sb, int index); -int *snapbuild_get_item_data(SNAPBUILD *sb, int key); -int snapbuild_finish(SNAPBUILD *sb, void *snapdata); -void *snapbuild_new_item(SNAPBUILD *sb, int type, int id, int size); #endif /* ENGINE_SNAPSHOT_H */ diff --git a/src/engine/server/es_register.c b/src/engine/server/es_register.cpp index b3ac70a6..0eb5dba5 100644 --- a/src/engine/server/es_register.c +++ b/src/engine/server/es_register.cpp @@ -6,7 +6,7 @@ #include <mastersrv/mastersrv.h> -extern NETSERVER *net; +extern CNetServer *m_pNetServer; enum { @@ -29,48 +29,48 @@ static void register_new_state(int state) register_state_start = time_get(); } -static void register_send_fwcheckresponse(NETADDR *addr) +static void register_send_fwcheckresponse(NETADDR *pAddr) { - NETCHUNK packet; - packet.client_id = -1; - packet.address = *addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_FWRESPONSE); - packet.data = SERVERBROWSE_FWRESPONSE; - netserver_send(net, &packet); + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = *pAddr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_FWRESPONSE); + Packet.m_pData = SERVERBROWSE_FWRESPONSE; + m_pNetServer->Send(&Packet); } static void register_send_heartbeat(NETADDR addr) { static unsigned char data[sizeof(SERVERBROWSE_HEARTBEAT) + 2]; unsigned short port = config.sv_port; - NETCHUNK packet; + CNetChunk Packet; mem_copy(data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)); - packet.client_id = -1; - packet.address = addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT) + 2; - packet.data = &data; + Packet.m_ClientID = -1; + Packet.m_Address = addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_HEARTBEAT) + 2; + Packet.m_pData = &data; /* supply the set port that the master can use if it has problems */ if(config.sv_external_port) port = config.sv_external_port; data[sizeof(SERVERBROWSE_HEARTBEAT)] = port >> 8; data[sizeof(SERVERBROWSE_HEARTBEAT)+1] = port&0xff; - netserver_send(net, &packet); + m_pNetServer->Send(&Packet); } -static void register_send_count_request(NETADDR addr) +static void register_send_count_request(NETADDR Addr) { - NETCHUNK packet; - packet.client_id = -1; - packet.address = addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = sizeof(SERVERBROWSE_GETCOUNT); - packet.data = SERVERBROWSE_GETCOUNT; - netserver_send(net, &packet); + CNetChunk Packet; + Packet.m_ClientID = -1; + Packet.m_Address = Addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = sizeof(SERVERBROWSE_GETCOUNT); + Packet.m_pData = SERVERBROWSE_GETCOUNT; + m_pNetServer->Send(&Packet); } typedef struct @@ -221,50 +221,49 @@ void register_update() } } -static void register_got_count(NETCHUNK *p) +static void register_got_count(CNetChunk *pChunk) { - unsigned char *data = (unsigned char *)p->data; - int count = (data[sizeof(SERVERBROWSE_COUNT)]<<8) | data[sizeof(SERVERBROWSE_COUNT)+1]; - int i; + unsigned char *pData = (unsigned char *)pChunk->m_pData; + int Count = (pData[sizeof(SERVERBROWSE_COUNT)]<<8) | pData[sizeof(SERVERBROWSE_COUNT)+1]; - for(i = 0; i < MAX_MASTERSERVERS; i++) + for(int i = 0; i < MAX_MASTERSERVERS; i++) { - if(net_addr_comp(&masterserver_info[i].addr, &p->address) == 0) + if(net_addr_comp(&masterserver_info[i].addr, &pChunk->m_Address) == 0) { - masterserver_info[i].count = count; + masterserver_info[i].count = Count; break; } } } -int register_process_packet(NETCHUNK *packet) +int register_process_packet(CNetChunk *pPacket) { - if(packet->data_size == sizeof(SERVERBROWSE_FWCHECK) && - memcmp(packet->data, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) + if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWCHECK) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWCHECK, sizeof(SERVERBROWSE_FWCHECK)) == 0) { - register_send_fwcheckresponse(&packet->address); + register_send_fwcheckresponse(&pPacket->m_Address); return 1; } - else if(packet->data_size == sizeof(SERVERBROWSE_FWOK) && - memcmp(packet->data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWOK) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0) { if(register_first) dbg_msg("register", "no firewall/nat problems detected"); register_new_state(REGISTERSTATE_REGISTERED); return 1; } - else if(packet->data_size == sizeof(SERVERBROWSE_FWERROR) && - memcmp(packet->data, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_FWERROR) && + memcmp(pPacket->m_pData, SERVERBROWSE_FWERROR, sizeof(SERVERBROWSE_FWERROR)) == 0) { dbg_msg("register", "ERROR: the master server reports that clients can not connect to this server."); dbg_msg("register", "ERROR: configure your firewall/nat to let through udp on port %d.", config.sv_port); register_new_state(REGISTERSTATE_ERROR); return 1; } - else if(packet->data_size == sizeof(SERVERBROWSE_COUNT)+2 && - memcmp(packet->data, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) + else if(pPacket->m_DataSize == sizeof(SERVERBROWSE_COUNT)+2 && + memcmp(pPacket->m_pData, SERVERBROWSE_COUNT, sizeof(SERVERBROWSE_COUNT)) == 0) { - register_got_count(packet); + register_got_count(pPacket); return 1; } diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c deleted file mode 100644 index 8763351d..00000000 --- a/src/engine/server/es_server.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ -#include <stdio.h> -#include <string.h> -#include <stdlib.h> - -#include <base/system.h> - -#include <engine/e_config.h> -#include <engine/e_engine.h> -#include <engine/e_server_interface.h> - -#include <engine/e_protocol.h> -#include <engine/e_snapshot.h> - -#include <engine/e_compression.h> - -#include <engine/e_network.h> -#include <engine/e_config.h> -#include <engine/e_packer.h> -#include <engine/e_datafile.h> -#include <engine/e_demorec.h> - -#include <mastersrv/mastersrv.h> - -#if defined(CONF_FAMILY_WINDOWS) - #define _WIN32_WINNT 0x0500 - #include <windows.h> -#endif - -static SNAPBUILD builder; - -static int64 game_start_time; -static int current_tick = 0; -static int run_server = 1; - -static char browseinfo_gametype[16] = {0}; -static int browseinfo_progression = -1; - -static int64 lastheartbeat; -/*static NETADDR4 master_server;*/ - -static char current_map[64]; -static int current_map_crc; -static unsigned char *current_map_data = 0; -static int current_map_size = 0; - -void *snap_new_item(int type, int id, int size) -{ - dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); - dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); - return snapbuild_new_item(&builder, type, id, size); -} - -typedef struct -{ - short next; - short state; /* 0 = free, 1 = alloced, 2 = timed */ - int timeout; -} SNAP_ID; - -static const int MAX_IDS = 16*1024; /* should be lowered */ -static SNAP_ID snap_ids[16*1024]; -static int snap_first_free_id; -static int snap_first_timed_id; -static int snap_last_timed_id; -static int snap_id_usage; -static int snap_id_inusage; -static int snap_id_inited = 0; - -enum -{ - SRVCLIENT_STATE_EMPTY = 0, - SRVCLIENT_STATE_AUTH, - SRVCLIENT_STATE_CONNECTING, - SRVCLIENT_STATE_READY, - SRVCLIENT_STATE_INGAME, - - SRVCLIENT_SNAPRATE_INIT=0, - SRVCLIENT_SNAPRATE_FULL, - SRVCLIENT_SNAPRATE_RECOVER -}; - -typedef struct -{ - int data[MAX_INPUT_SIZE]; - int game_tick; /* the tick that was chosen for the input */ -} CLIENT_INPUT; - -/* */ -typedef struct -{ - /* connection state info */ - int state; - int latency; - int snap_rate; - - int last_acked_snapshot; - int last_input_tick; - SNAPSTORAGE snapshots; - - CLIENT_INPUT latestinput; - CLIENT_INPUT inputs[200]; /* TODO: handle input better */ - int current_input; - - char name[MAX_NAME_LENGTH]; - char clan[MAX_CLANNAME_LENGTH]; - int score; - int authed; -} CLIENT; - -static CLIENT clients[MAX_CLIENTS]; -NETSERVER *net; - -static void snap_init_id() -{ - int i; - if(snap_id_inited) - return; - - for(i = 0; i < MAX_IDS; i++) - { - snap_ids[i].next = i+1; - snap_ids[i].state = 0; - } - - snap_ids[MAX_IDS-1].next = -1; - snap_first_free_id = 0; - snap_first_timed_id = -1; - snap_last_timed_id = -1; - snap_id_usage = 0; - snap_id_inusage = 0; - - snap_id_inited = 1; -} - -static void snap_remove_first_timeout() -{ - int next_timed = snap_ids[snap_first_timed_id].next; - - /* add it to the free list */ - snap_ids[snap_first_timed_id].next = snap_first_free_id; - snap_ids[snap_first_timed_id].state = 0; - snap_first_free_id = snap_first_timed_id; - - /* remove it from the timed list */ - snap_first_timed_id = next_timed; - if(snap_first_timed_id == -1) - snap_last_timed_id = -1; - - snap_id_usage--; -} - -int snap_new_id() -{ - int id; - int64 now = time_get(); - if(!snap_id_inited) - snap_init_id(); - - /* process timed ids */ - while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout < now) - snap_remove_first_timeout(); - - id = snap_first_free_id; - dbg_assert(id != -1, "id error"); - snap_first_free_id = snap_ids[snap_first_free_id].next; - snap_ids[id].state = 1; - snap_id_usage++; - snap_id_inusage++; - return id; -} - -void snap_timeout_ids() -{ - /* process timed ids */ - while(snap_first_timed_id != -1) - snap_remove_first_timeout(); -} - -void snap_free_id(int id) -{ - dbg_assert(snap_ids[id].state == 1, "id is not alloced"); - - snap_id_inusage--; - snap_ids[id].state = 2; - snap_ids[id].timeout = time_get()+time_freq()*5; - snap_ids[id].next = -1; - - if(snap_last_timed_id != -1) - { - snap_ids[snap_last_timed_id].next = id; - snap_last_timed_id = id; - } - else - { - snap_first_timed_id = id; - snap_last_timed_id = id; - } -} - -int *server_latestinput(int client_id, int *size) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return 0; - return clients[client_id].latestinput.data; -} - -const char *server_clientname(int client_id) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return "(invalid client)"; - return clients[client_id].name; -} - -static const char *str_ltrim(const char *str) -{ - while(*str && *str <= 32) - str++; - return str; -} - -static void str_rtrim(char *str) -{ - int i = str_length(str); - while(i >= 0) - { - if(str[i] > 32) - break; - str[i] = 0; - i--; - } -} - -static int server_try_setclientname(int client_id, const char *name) -{ - int i; - char trimmed_name[64]; - - /* trim the name */ - str_copy(trimmed_name, str_ltrim(name), sizeof(trimmed_name)); - str_rtrim(trimmed_name); - dbg_msg("", "'%s' -> '%s'", name, trimmed_name); - name = trimmed_name; - - - /* check for empty names */ - if(!name[0]) - return -1; - - /* make sure that two clients doesn't have the same name */ - for(i = 0; i < MAX_CLIENTS; i++) - if(i != client_id && clients[i].state >= SRVCLIENT_STATE_READY) - { - if(strcmp(name, clients[i].name) == 0) - return -1; - } - - /* set the client name */ - str_copy(clients[client_id].name, name, MAX_NAME_LENGTH); - return 0; -} - -void server_setclientname(int client_id, const char *name) -{ - char nametry[MAX_NAME_LENGTH]; - int i; - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return; - - if(!name) - return; - - str_copy(nametry, name, MAX_NAME_LENGTH); - if(server_try_setclientname(client_id, nametry)) - { - /* auto rename */ - for(i = 1;; i++) - { - str_format(nametry, MAX_NAME_LENGTH, "(%d)%s", i, name); - if(server_try_setclientname(client_id, nametry) == 0) - break; - } - } -} - -void server_setclientscore(int client_id, int score) -{ - if(client_id < 0 || client_id > MAX_CLIENTS || clients[client_id].state < SRVCLIENT_STATE_READY) - return; - clients[client_id].score = score; -} - -void server_setbrowseinfo(const char *game_type, int progression) -{ - str_copy(browseinfo_gametype, game_type, sizeof(browseinfo_gametype)); - browseinfo_progression = progression; - if(browseinfo_progression > 100) - browseinfo_progression = 100; - if(browseinfo_progression < -1) - browseinfo_progression = -1; -} - -void server_kick(int client_id, const char *reason) -{ - if(client_id < 0 || client_id > MAX_CLIENTS) - return; - - if(clients[client_id].state != SRVCLIENT_STATE_EMPTY) - netserver_drop(net, client_id, reason); -} - -int server_tick() -{ - return current_tick; -} - -int64 server_tick_start_time(int tick) -{ - return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED; -} - -int server_tickspeed() -{ - return SERVER_TICK_SPEED; -} - -int server_init() -{ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - clients[i].state = SRVCLIENT_STATE_EMPTY; - clients[i].name[0] = 0; - clients[i].clan[0] = 0; - snapstorage_init(&clients[i].snapshots); - } - - current_tick = 0; - - return 0; -} - -int server_getclientinfo(int client_id, CLIENT_INFO *info) -{ - dbg_assert(client_id >= 0 && client_id < MAX_CLIENTS, "client_id is not valid"); - dbg_assert(info != 0, "info can not be null"); - - if(clients[client_id].state == SRVCLIENT_STATE_INGAME) - { - info->name = clients[client_id].name; - info->latency = clients[client_id].latency; - return 1; - } - return 0; -} - -int server_send_msg(int client_id) -{ - const MSG_INFO *info = msg_get_info(); - NETCHUNK packet; - if(!info) - return -1; - - mem_zero(&packet, sizeof(NETCHUNK)); - - packet.client_id = client_id; - packet.data = info->data; - packet.data_size = info->size; - - if(info->flags&MSGFLAG_VITAL) - packet.flags |= NETSENDFLAG_VITAL; - if(info->flags&MSGFLAG_FLUSH) - packet.flags |= NETSENDFLAG_FLUSH; - - /* write message to demo recorder */ - if(!(info->flags&MSGFLAG_NORECORD)) - demorec_record_message(info->data, info->size); - - if(!(info->flags&MSGFLAG_NOSEND)) - { - if(client_id == -1) - { - /* broadcast */ - int i; - for(i = 0; i < MAX_CLIENTS; i++) - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - packet.client_id = i; - netserver_send(net, &packet); - } - } - else - netserver_send(net, &packet); - } - return 0; -} - -static void server_do_snap() -{ - int i; - - { - static PERFORMACE_INFO scope = {"presnap", 0}; - perf_start(&scope); - mods_presnap(); - perf_end(); - } - - /* create snapshot for demo recording */ - if(demorec_isrecording()) - { - char data[MAX_SNAPSHOT_SIZE]; - int snapshot_size; - - /* build snap and possibly add some messages */ - snapbuild_init(&builder); - mods_snap(-1); - snapshot_size = snapbuild_finish(&builder, data); - - /* write snapshot */ - demorec_record_snapshot(server_tick(), data, snapshot_size); - } - - /* create snapshots for all clients */ - for(i = 0; i < MAX_CLIENTS; i++) - { - /* client must be ingame to recive snapshots */ - if(clients[i].state != SRVCLIENT_STATE_INGAME) - continue; - - /* this client is trying to recover, don't spam snapshots */ - if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_RECOVER && (server_tick()%50) != 0) - continue; - - /* this client is trying to recover, don't spam snapshots */ - if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_INIT && (server_tick()%10) != 0) - continue; - - { - char data[MAX_SNAPSHOT_SIZE]; - char deltadata[MAX_SNAPSHOT_SIZE]; - char compdata[MAX_SNAPSHOT_SIZE]; - int snapshot_size; - int crc; - static SNAPSHOT emptysnap; - SNAPSHOT *deltashot = &emptysnap; - int deltashot_size; - int delta_tick = -1; - int deltasize; - static PERFORMACE_INFO scope = {"build", 0}; - perf_start(&scope); - - snapbuild_init(&builder); - - { - static PERFORMACE_INFO scope = {"modsnap", 0}; - perf_start(&scope); - mods_snap(i); - perf_end(); - } - - /* finish snapshot */ - snapshot_size = snapbuild_finish(&builder, data); - crc = snapshot_crc((SNAPSHOT*)data); - - /* remove old snapshos */ - /* keep 3 seconds worth of snapshots */ - snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED*3); - - /* save it the snapshot */ - snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data, 0); - - /* find snapshot that we can preform delta against */ - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - { - deltashot_size = snapstorage_get(&clients[i].snapshots, clients[i].last_acked_snapshot, 0, &deltashot, 0); - if(deltashot_size >= 0) - delta_tick = clients[i].last_acked_snapshot; - else - { - /* no acked package found, force client to recover rate */ - if(clients[i].snap_rate == SRVCLIENT_SNAPRATE_FULL) - clients[i].snap_rate = SRVCLIENT_SNAPRATE_RECOVER; - } - } - - /* create delta */ - { - static PERFORMACE_INFO scope = {"delta", 0}; - perf_start(&scope); - deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata); - perf_end(); - } - - - if(deltasize) - { - /* compress it */ - int snapshot_size; - const int max_size = MAX_SNAPSHOT_PACKSIZE; - int numpackets; - int n, left; - - { - static PERFORMACE_INFO scope = {"compress", 0}; - /*char buffer[512];*/ - perf_start(&scope); - snapshot_size = intpack_compress(deltadata, deltasize, compdata); - - /*str_hex(buffer, sizeof(buffer), compdata, snapshot_size); - dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/ - - perf_end(); - } - - numpackets = (snapshot_size+max_size-1)/max_size; - - for(n = 0, left = snapshot_size; left; n++) - { - int chunk = left < max_size ? left : max_size; - left -= chunk; - - if(numpackets == 1) - msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH); - else - msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH); - - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - - if(numpackets != 1) - { - msg_pack_int(numpackets); - msg_pack_int(n); - } - - msg_pack_int(crc); - msg_pack_int(chunk); - msg_pack_raw(&compdata[n*max_size], chunk); - msg_pack_end(); - server_send_msg(i); - } - } - else - { - msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH); - msg_pack_int(current_tick); - msg_pack_int(current_tick-delta_tick); /* compressed with */ - msg_pack_end(); - server_send_msg(i); - } - - perf_end(); - } - } - - mods_postsnap(); -} - - -static void reset_client(int cid) -{ - /* reset input */ - int i; - for(i = 0; i < 200; i++) - clients[cid].inputs[i].game_tick = -1; - clients[cid].current_input = 0; - mem_zero(&clients[cid].latestinput, sizeof(clients[cid].latestinput)); - - snapstorage_purge_all(&clients[cid].snapshots); - clients[cid].last_acked_snapshot = -1; - clients[cid].last_input_tick = -1; - clients[cid].snap_rate = SRVCLIENT_SNAPRATE_INIT; - clients[cid].score = 0; - -} - -static int new_client_callback(int cid, void *user) -{ - clients[cid].state = SRVCLIENT_STATE_AUTH; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - clients[cid].authed = 0; - reset_client(cid); - return 0; -} - -static int del_client_callback(int cid, void *user) -{ - /* notify the mod about the drop */ - if(clients[cid].state >= SRVCLIENT_STATE_READY) - mods_client_drop(cid); - - clients[cid].state = SRVCLIENT_STATE_EMPTY; - clients[cid].name[0] = 0; - clients[cid].clan[0] = 0; - clients[cid].authed = 0; - snapstorage_purge_all(&clients[cid].snapshots); - return 0; -} - -static void server_send_map(int cid) -{ - msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_string(config.sv_map, 0); - msg_pack_int(current_map_crc); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_rcon_line(int cid, const char *line) -{ - msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); - msg_pack_string(line, 512); - msg_pack_end(); - server_send_msg(cid); -} - -static void server_send_rcon_line_authed(const char *line, void *user_data) -{ - static volatile int reentry_guard = 0; - int i; - - if(reentry_guard) return; - reentry_guard++; - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY && clients[i].authed) - server_send_rcon_line(i, line); - } - - reentry_guard--; -} - -static void server_process_client_packet(NETCHUNK *packet) -{ - int cid = packet->client_id; - NETADDR addr; - - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - - if(clients[cid].state == SRVCLIENT_STATE_AUTH) - { - if(sys && msg == NETMSG_INFO) - { - char version[64]; - const char *password; - str_copy(version, msg_unpack_string(), 64); - if(strcmp(version, mods_net_version()) != 0) - { - /* OH FUCK! wrong version, drop him */ - char reason[256]; - str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version); - netserver_drop(net, cid, reason); - return; - } - - str_copy(clients[cid].name, msg_unpack_string(), MAX_NAME_LENGTH); - str_copy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH); - password = msg_unpack_string(); - - if(config.password[0] != 0 && strcmp(config.password, password) != 0) - { - /* wrong password */ - netserver_drop(net, cid, "wrong password"); - return; - } - - clients[cid].state = SRVCLIENT_STATE_CONNECTING; - server_send_map(cid); - } - } - else - { - if(sys) - { - /* system message */ - if(msg == NETMSG_REQUEST_MAP_DATA) - { - int chunk = msg_unpack_int(); - int chunk_size = 1024-128; - int offset = chunk * chunk_size; - int last = 0; - - /* drop faulty map data requests */ - if(chunk < 0 || offset > current_map_size) - return; - - if(offset+chunk_size >= current_map_size) - { - chunk_size = current_map_size-offset; - if(chunk_size < 0) - chunk_size = 0; - last = 1; - } - - msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); - msg_pack_int(last); - msg_pack_int(current_map_size); - msg_pack_int(chunk_size); - msg_pack_raw(¤t_map_data[offset], chunk_size); - msg_pack_end(); - server_send_msg(cid); - - if(config.debug) - dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); - } - else if(msg == NETMSG_READY) - { - if(clients[cid].state == SRVCLIENT_STATE_CONNECTING) - { - netserver_client_addr(net, cid, &addr); - - dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d", - cid, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] - ); - clients[cid].state = SRVCLIENT_STATE_READY; - mods_connected(cid); - } - } - else if(msg == NETMSG_ENTERGAME) - { - if(clients[cid].state == SRVCLIENT_STATE_READY) - { - netserver_client_addr(net, cid, &addr); - - dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d", - cid, - addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] - ); - clients[cid].state = SRVCLIENT_STATE_INGAME; - mods_client_enter(cid); - } - } - else if(msg == NETMSG_INPUT) - { - int tick, size, i; - CLIENT_INPUT *input; - int64 tagtime; - - clients[cid].last_acked_snapshot = msg_unpack_int(); - tick = msg_unpack_int(); - size = msg_unpack_int(); - - /* check for errors */ - if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) - return; - - if(clients[cid].last_acked_snapshot > 0) - clients[cid].snap_rate = SRVCLIENT_SNAPRATE_FULL; - - if(snapstorage_get(&clients[cid].snapshots, clients[cid].last_acked_snapshot, &tagtime, 0, 0) >= 0) - clients[cid].latency = (int)(((time_get()-tagtime)*1000)/time_freq()); - - /* add message to report the input timing */ - /* skip packets that are old */ - if(tick > clients[cid].last_input_tick) - { - int time_left = ((server_tick_start_time(tick)-time_get())*1000) / time_freq(); - msg_pack_start_system(NETMSG_INPUTTIMING, 0); - msg_pack_int(tick); - msg_pack_int(time_left); - msg_pack_end(); - server_send_msg(cid); - } - - clients[cid].last_input_tick = tick; - - input = &clients[cid].inputs[clients[cid].current_input]; - - if(tick <= server_tick()) - tick = server_tick()+1; - - input->game_tick = tick; - - for(i = 0; i < size/4; i++) - input->data[i] = msg_unpack_int(); - - mem_copy(clients[cid].latestinput.data, input->data, MAX_INPUT_SIZE*sizeof(int)); - - clients[cid].current_input++; - clients[cid].current_input %= 200; - - /* call the mod with the fresh input data */ - if(clients[cid].state == SRVCLIENT_STATE_INGAME) - mods_client_direct_input(cid, clients[cid].latestinput.data); - } - else if(msg == NETMSG_RCON_CMD) - { - const char *cmd = msg_unpack_string(); - - if(msg_unpack_error() == 0 && clients[cid].authed) - { - dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); - console_execute_line(cmd); - } - } - else if(msg == NETMSG_RCON_AUTH) - { - const char *pw; - msg_unpack_string(); /* login name, not used */ - pw = msg_unpack_string(); - - if(msg_unpack_error() == 0) - { - if(config.sv_rcon_password[0] == 0) - { - server_send_rcon_line(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); - } - else if(strcmp(pw, config.sv_rcon_password) == 0) - { - msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL); - msg_pack_int(1); - msg_pack_end(); - server_send_msg(cid); - - clients[cid].authed = 1; - server_send_rcon_line(cid, "Authentication successful. Remote console access granted."); - dbg_msg("server", "cid=%d authed", cid); - } - else - { - server_send_rcon_line(cid, "Wrong password."); - } - } - } - else if(msg == NETMSG_PING) - { - msg_pack_start_system(NETMSG_PING_REPLY, 0); - msg_pack_end(); - server_send_msg(cid); - } - else - { - char hex[] = "0123456789ABCDEF"; - char buf[512]; - int b; - - for(b = 0; b < packet->data_size && b < 32; b++) - { - buf[b*3] = hex[((const unsigned char *)packet->data)[b]>>4]; - buf[b*3+1] = hex[((const unsigned char *)packet->data)[b]&0xf]; - buf[b*3+2] = ' '; - buf[b*3+3] = 0; - } - - dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size); - dbg_msg("server", "%s", buf); - - } - } - else - { - /* game message */ - if(clients[cid].state >= SRVCLIENT_STATE_READY) - mods_message(msg, cid); - } - } -} - - -int server_ban_add(NETADDR addr, int seconds) -{ - return netserver_ban_add(net, addr, seconds); -} - -int server_ban_remove(NETADDR addr) -{ - return netserver_ban_remove(net, addr); -} - -static void server_send_serverinfo(NETADDR *addr, int token) -{ - NETCHUNK packet; - PACKER p; - char buf[128]; - - /* count the players */ - int player_count = 0; - int i; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - player_count++; - } - - packer_reset(&p); - - if(token >= 0) - { - /* new token based format */ - packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); - str_format(buf, sizeof(buf), "%d", token); - packer_add_string(&p, buf, 6); - } - else - { - /* old format */ - packer_add_raw(&p, SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)); - } - - packer_add_string(&p, mods_version(), 32); - packer_add_string(&p, config.sv_name, 64); - packer_add_string(&p, config.sv_map, 32); - - /* gametype */ - packer_add_string(&p, browseinfo_gametype, 16); - - /* flags */ - i = 0; - if(config.password[0]) /* password set */ - i |= SRVFLAG_PASSWORD; - str_format(buf, sizeof(buf), "%d", i); - packer_add_string(&p, buf, 2); - - /* progression */ - str_format(buf, sizeof(buf), "%d", browseinfo_progression); - packer_add_string(&p, buf, 4); - - str_format(buf, sizeof(buf), "%d", player_count); packer_add_string(&p, buf, 3); /* num players */ - str_format(buf, sizeof(buf), "%d", netserver_max_clients(net)); packer_add_string(&p, buf, 3); /* max players */ - - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state != SRVCLIENT_STATE_EMPTY) - { - packer_add_string(&p, clients[i].name, 48); /* player name */ - str_format(buf, sizeof(buf), "%d", clients[i].score); packer_add_string(&p, buf, 6); /* player score */ - } - } - - - packet.client_id = -1; - packet.address = *addr; - packet.flags = NETSENDFLAG_CONNLESS; - packet.data_size = packer_size(&p); - packet.data = packer_data(&p); - netserver_send(net, &packet); -} - -extern int register_process_packet(NETCHUNK *packet); -extern int register_update(); - -static void server_pump_network() -{ - NETCHUNK packet; - - netserver_update(net); - - /* process packets */ - while(netserver_recv(net, &packet)) - { - if(packet.client_id == -1) - { - /* stateless */ - if(!register_process_packet(&packet)) - { - if(packet.data_size == sizeof(SERVERBROWSE_GETINFO)+1 && - memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) - { - server_send_serverinfo(&packet.address, ((unsigned char *)packet.data)[sizeof(SERVERBROWSE_GETINFO)]); - } - - - if(packet.data_size == sizeof(SERVERBROWSE_OLD_GETINFO) && - memcmp(packet.data, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) - { - server_send_serverinfo(&packet.address, -1); - } - } - } - else - server_process_client_packet(&packet); - } -} - -static int server_load_map(const char *mapname) -{ - DATAFILE *df; - char buf[512]; - str_format(buf, sizeof(buf), "maps/%s.map", mapname); - df = datafile_load(buf); - if(!df) - return 0; - - /* stop recording when we change map */ - demorec_record_stop(); - - /* reinit snapshot ids */ - snap_timeout_ids(); - - /* get the crc of the map */ - current_map_crc = datafile_crc(buf); - dbg_msg("server", "%s crc is %08x", buf, current_map_crc); - - str_copy(current_map, mapname, sizeof(current_map)); - map_set(df); - - /* load compelate map into memory for download */ - { - IOHANDLE file = engine_openfile(buf, IOFLAG_READ); - current_map_size = (int)io_length(file); - if(current_map_data) - mem_free(current_map_data); - current_map_data = (unsigned char *)mem_alloc(current_map_size, 1); - io_read(file, current_map_data, current_map_size); - io_close(file); - } - return 1; -} - -static int server_run() -{ - NETADDR bindaddr; - - snap_init_id(); - net_init(); - - /* */ - console_register_print_callback(server_send_rcon_line_authed, 0); - - /* load map */ - if(!server_load_map(config.sv_map)) - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - return -1; - } - - /* start server */ - /* TODO: IPv6 support */ - if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) - { - /* sweet! */ - bindaddr.port = config.sv_port; - } - else - { - mem_zero(&bindaddr, sizeof(bindaddr)); - bindaddr.port = config.sv_port; - } - - net = netserver_open(bindaddr, config.sv_max_clients, 0); - if(!net) - { - dbg_msg("server", "couldn't open socket. port might already be in use"); - return -1; - } - - netserver_set_callbacks(net, new_client_callback, del_client_callback, 0); - - dbg_msg("server", "server name is '%s'", config.sv_name); - - mods_init(); - dbg_msg("server", "version %s", mods_net_version()); - - /* start game */ - { - int64 reporttime = time_get(); - int reportinterval = 3; - - lastheartbeat = 0; - game_start_time = time_get(); - - if(config.debug) - dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); - - while(run_server) - { - static PERFORMACE_INFO rootscope = {"root", 0}; - int64 t = time_get(); - int new_ticks = 0; - - perf_start(&rootscope); - - /* load new map TODO: don't poll this */ - if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) - { - config.sv_map_reload = 0; - - /* load map */ - if(server_load_map(config.sv_map)) - { - int c; - - /* new map loaded */ - mods_shutdown(); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - - server_send_map(c); - reset_client(c); - clients[c].state = SRVCLIENT_STATE_CONNECTING; - } - - game_start_time = time_get(); - current_tick = 0; - mods_init(); - } - else - { - dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); - config_set_sv_map(&config, current_map); - } - } - - while(t > server_tick_start_time(current_tick+1)) - { - current_tick++; - new_ticks++; - - /* apply new input */ - { - static PERFORMACE_INFO scope = {"input", 0}; - int c, i; - - perf_start(&scope); - - for(c = 0; c < MAX_CLIENTS; c++) - { - if(clients[c].state == SRVCLIENT_STATE_EMPTY) - continue; - for(i = 0; i < 200; i++) - { - if(clients[c].inputs[i].game_tick == server_tick()) - { - if(clients[c].state == SRVCLIENT_STATE_INGAME) - mods_client_predicted_input(c, clients[c].inputs[i].data); - break; - } - } - } - - perf_end(); - } - - /* progress game */ - { - static PERFORMACE_INFO scope = {"tick", 0}; - perf_start(&scope); - mods_tick(); - perf_end(); - } - } - - /* snap game */ - if(new_ticks) - { - if(config.sv_high_bandwidth || (current_tick%2) == 0) - { - static PERFORMACE_INFO scope = {"snap", 0}; - perf_start(&scope); - server_do_snap(); - perf_end(); - } - } - - /* master server stuff */ - register_update(); - - - { - static PERFORMACE_INFO scope = {"net", 0}; - perf_start(&scope); - server_pump_network(); - perf_end(); - } - - perf_end(); - - if(reporttime < time_get()) - { - if(config.debug) - { - /* - static NETSTATS prev_stats; - NETSTATS stats; - netserver_stats(net, &stats); - - perf_next(); - - if(config.dbg_pref) - perf_dump(&rootscope); - - dbg_msg("server", "send=%8d recv=%8d", - (stats.send_bytes - prev_stats.send_bytes)/reportinterval, - (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); - - prev_stats = stats; - */ - } - - reporttime += time_freq()*reportinterval; - } - - /* wait for incomming data */ - net_socket_read_wait(netserver_socket(net), 5); - } - } - - mods_shutdown(); - map_unload(); - - if(current_map_data) - mem_free(current_map_data); - return 0; -} - -static void con_kick(void *result, void *user_data) -{ - server_kick(console_arg_int(result, 0), "kicked by console"); -} - -static int str_allnum(const char *str) -{ - while(*str) - { - if(!(*str >= '0' && *str <= '9')) - return 0; - str++; - } - return 1; -} - -static void con_ban(void *result, void *user_data) -{ - NETADDR addr; - char addrstr[128]; - const char *str = console_arg_string(result, 0); - int minutes = 30; - - if(console_arg_num(result) > 1) - minutes = console_arg_int(result, 1); - - if(net_addr_from_str(&addr, str) == 0) - server_ban_add(addr, minutes*60); - else if(str_allnum(str)) - { - NETADDR addr; - int cid = atoi(str); - - if(cid < 0 || cid > MAX_CLIENTS || clients[cid].state == SRVCLIENT_STATE_EMPTY) - { - dbg_msg("server", "invalid client id"); - return; - } - - netserver_client_addr(net, cid, &addr); - server_ban_add(addr, minutes*60); - } - - addr.port = 0; - net_addr_str(&addr, addrstr, sizeof(addrstr)); - - if(minutes) - dbg_msg("server", "banned %s for %d minutes", addrstr, minutes); - else - dbg_msg("server", "banned %s for life", addrstr); -} - -static void con_unban(void *result, void *user_data) -{ - NETADDR addr; - const char *str = console_arg_string(result, 0); - - if(net_addr_from_str(&addr, str) == 0) - server_ban_remove(addr); - else - dbg_msg("server", "invalid network address"); -} - -static void con_bans(void *result, void *user_data) -{ - int i; - unsigned now = time_timestamp(); - NETBANINFO info; - NETADDR addr; - int num = netserver_ban_num(net); - for(i = 0; i < num; i++) - { - unsigned t; - netserver_ban_get(net, i, &info); - addr = info.addr; - - if(info.expires == 0xffffffff) - { - dbg_msg("server", "#%d %d.%d.%d.%d for life", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3]); - } - else - { - t = info.expires - now; - dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], t/60, t%60); - } - } - dbg_msg("server", "%d ban(s)", num); -} - -static void con_status(void *result, void *user_data) -{ - int i; - NETADDR addr; - for(i = 0; i < MAX_CLIENTS; i++) - { - if(clients[i].state == SRVCLIENT_STATE_INGAME) - { - netserver_client_addr(net, i, &addr); - dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d", - i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port, - clients[i].name, clients[i].score); - } - } -} - -static void con_shutdown(void *result, void *user_data) -{ - run_server = 0; -} - -static void con_record(void *result, void *user_data) -{ - char filename[512]; - str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(result, 0)); - demorec_record_start(filename, mods_net_version(), current_map, current_map_crc, "server"); -} - -static void con_stoprecord(void *result, void *user_data) -{ - demorec_record_stop(); -} - -static void server_register_commands() -{ - MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, con_kick, 0, ""); - MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, con_ban, 0, ""); - MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, con_unban, 0, ""); - MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, con_bans, 0, ""); - MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, con_status, 0, ""); - MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, con_shutdown, 0, ""); - - MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, con_record, 0, ""); - MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, con_stoprecord, 0, ""); -} - -int main(int argc, char **argv) -{ -#if defined(CONF_FAMILY_WINDOWS) - int i; - for(i = 1; i < argc; i++) - { - if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0) - { - ShowWindow(GetConsoleWindow(), SW_HIDE); - break; - } - } -#endif - - /* init the engine */ - dbg_msg("server", "starting..."); - engine_init("Teeworlds"); - - /* register all console commands */ - server_register_commands(); - mods_console_init(); - - /* parse the command line arguments */ - engine_parse_arguments(argc, argv); - - /* run the server */ - server_run(); - return 0; -} - diff --git a/src/engine/server/es_server.cpp b/src/engine/server/es_server.cpp new file mode 100644 index 00000000..cc0e6e0f --- /dev/null +++ b/src/engine/server/es_server.cpp @@ -0,0 +1,1969 @@ +/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <base/system.h> + +#include <engine/e_config.h> +#include <engine/e_engine.h> +#include <engine/e_server_interface.h> + +#include <engine/e_protocol.h> +#include <engine/e_snapshot.h> + +#include <engine/e_compression.h> + +#include <engine/e_network.h> +#include <engine/e_config.h> +#include <engine/e_packer.h> +#include <engine/e_datafile.h> +#include <engine/e_demorec.h> + +#include <mastersrv/mastersrv.h> + +#if defined(CONF_FAMILY_WINDOWS) + #define _WIN32_WINNT 0x0500 + #include <windows.h> +#endif + + +extern int register_process_packet(CNetChunk *pPacket); +extern int register_update(); + +static const char *str_ltrim(const char *str) +{ + while(*str && *str <= 32) + str++; + return str; +} + +static void str_rtrim(char *str) +{ + int i = str_length(str); + while(i >= 0) + { + if(str[i] > 32) + break; + str[i] = 0; + i--; + } +} + + +class CSnapIDPool +{ + enum + { + MAX_IDS = 16*1024, + }; + + class CID + { + public: + short m_Next; + short m_State; /* 0 = free, 1 = alloced, 2 = timed */ + int m_Timeout; + }; + + CID m_aIDs[MAX_IDS]; + + int m_FirstFree; + int m_FirstTimed; + int m_LastTimed; + int m_Usage; + int m_InUsage; + +public: + + CSnapIDPool() + { + Reset(); + } + + void Reset() + { + for(int i = 0; i < MAX_IDS; i++) + { + m_aIDs[i].m_Next = i+1; + m_aIDs[i].m_State = 0; + } + + m_aIDs[MAX_IDS-1].m_Next = -1; + m_FirstFree = 0; + m_FirstTimed = -1; + m_LastTimed = -1; + m_Usage = 0; + m_InUsage = 0; + } + + + void RemoveFirstTimeout() + { + int NextTimed = m_aIDs[m_FirstTimed].m_Next; + + /* add it to the free list */ + m_aIDs[m_FirstTimed].m_Next = m_FirstFree; + m_aIDs[m_FirstTimed].m_State = 0; + m_FirstFree = m_FirstTimed; + + /* remove it from the timed list */ + m_FirstTimed = NextTimed; + if(m_FirstTimed == -1) + m_LastTimed = -1; + + m_Usage--; + } + + int NewID() + { + int64 now = time_get(); + + /* process timed ids */ + while(m_FirstTimed != -1 && m_aIDs[m_FirstTimed].m_Timeout < now) + RemoveFirstTimeout(); + + int id = m_FirstFree; + dbg_assert(id != -1, "id error"); + m_FirstFree = m_aIDs[m_FirstFree].m_Next; + m_aIDs[id].m_State = 1; + m_Usage++; + m_InUsage++; + return id; + } + + void TimeoutIDs() + { + /* process timed ids */ + while(m_FirstTimed != -1) + RemoveFirstTimeout(); + } + + void FreeID(int id) + { + dbg_assert(m_aIDs[id].m_State == 1, "id is not alloced"); + + m_InUsage--; + m_aIDs[id].m_State = 2; + m_aIDs[id].m_Timeout = time_get()+time_freq()*5; + m_aIDs[id].m_Next = -1; + + if(m_LastTimed != -1) + { + m_aIDs[m_LastTimed].m_Next = id; + m_LastTimed = id; + } + else + { + m_FirstTimed = id; + m_LastTimed = id; + } + } +}; + +#if 0 +class IGameServer +{ +public: + /* + Function: mods_console_init + TODO + */ + virtual void ConsoleInit(); + + /* + Function: Init + Called when the server is started. + + Remarks: + It's called after the map is loaded so all map items are available. + */ + void Init() = 0; + + /* + Function: Shutdown + Called when the server quits. + + Remarks: + Should be used to clean up all resources used. + */ + void Shutdown() = 0; + + /* + Function: ClientEnter + Called when a client has joined the game. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + It's called when the client is finished loading and should enter gameplay. + */ + void ClientEnter(int cid) = 0; + + /* + Function: ClientDrop + Called when a client drops from the server. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS + */ + void ClientDrop(int cid) = 0; + + /* + Function: ClientDirectInput + Called when the server recives new input from a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + input - Pointer to the input data. + size - Size of the data. (NOT IMPLEMENTED YET) + */ + void ClientDirectInput(int cid, void *input) = 0; + + /* + Function: ClientPredictedInput + Called when the server applys the predicted input on the client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + input - Pointer to the input data. + size - Size of the data. (NOT IMPLEMENTED YET) + */ + void ClientPredictedInput(int cid, void *input) = 0; + + + /* + Function: Tick + Called with a regular interval to progress the gameplay. + + Remarks: + The SERVER_TICK_SPEED tells the number of ticks per second. + */ + void Tick() = 0; + + /* + Function: Presnap + Called before the server starts to construct snapshots for the clients. + */ + void Presnap() = 0; + + /* + Function: Snap + Called to create the snapshot for a client. + + Arguments: + cid - Client ID. Is 0 - MAX_CLIENTS. + + Remarks: + The game should make a series of calls to <snap_new_item> to construct + the snapshot for the client. + */ + void Snap(int cid) = 0; + + /* + Function: PostSnap + Called after the server is done sending the snapshots. + */ + void PostSnap() = 0; + + /* + Function: ClientConnected + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + <other_func> + */ + void ClientConnected(int client_id) = 0; + + + /* + Function: NetVersion + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + <other_func> + */ + const char *NetVersion() = 0; + + /* + Function: Version + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + <other_func> + */ + const char *Version() = 0; + + /* + Function: Message + TODO + + Arguments: + arg1 - desc + + Returns: + + See Also: + <other_func> + */ + void Message(int msg, int client_id) = 0; +}; + +#endif +/* +class IServer +{ +public: + BanAdd + BanRemove + TickSpeed + Tick + Kick + SetBrowseInfo + SetClientScore + SetClientName + GetClientInfo + LatestInput + ClientName + + SendMessage() + + Map + + virtual int NewSnapID() = 0; + virtual int FreeSnapID(int i) = 0; +};*/ + +class CServer +{ +public: + /* */ + class CClient + { + public: + + enum + { + STATE_EMPTY = 0, + STATE_AUTH, + STATE_CONNECTING, + STATE_READY, + STATE_INGAME, + + SNAPRATE_INIT=0, + SNAPRATE_FULL, + SNAPRATE_RECOVER + }; + + class CInput + { + public: + int m_aData[MAX_INPUT_SIZE]; + int m_GameTick; /* the tick that was chosen for the input */ + }; + + /* connection state info */ + int m_State; + int m_Latency; + int m_SnapRate; + + int m_LastAckedSnapshot; + int m_LastInputTick; + CSnapshotStorage m_Snapshots; + + CInput m_LatestInput; + CInput m_aInputs[200]; /* TODO: handle input better */ + int m_CurrentInput; + + char m_aName[MAX_NAME_LENGTH]; + char m_aClan[MAX_CLANNAME_LENGTH]; + int m_Score; + int m_Authed; + + void Reset() + { + /* reset input */ + for(int i = 0; i < 200; i++) + m_aInputs[i].m_GameTick = -1; + m_CurrentInput = 0; + mem_zero(&m_LatestInput, sizeof(m_LatestInput)); + + m_Snapshots.PurgeAll(); + m_LastAckedSnapshot = -1; + m_LastInputTick = -1; + m_SnapRate = CClient::SNAPRATE_INIT; + m_Score = 0; + } + }; + + CClient m_aClients[MAX_CLIENTS]; + + CSnapshotBuilder m_SnapshotBuilder; + CSnapIDPool m_IDPool; + CNetServer m_NetServer; + + int64 m_GameStartTime; + int m_CurrentTick; + int m_RunServer; + + char m_aBrowseinfoGametype[16]; + int m_BrowseinfoProgression; + + int64 m_Lastheartbeat; + /*static NETADDR4 master_server;*/ + + char m_aCurrentMap[64]; + int m_CurrentMapCrc; + unsigned char *m_pCurrentMapData; + int m_CurrentMapSize; + + CServer() + { + m_CurrentTick = 0; + m_RunServer = 1; + + mem_zero(m_aBrowseinfoGametype, sizeof(m_aBrowseinfoGametype)); + m_BrowseinfoProgression = -1; + + m_pCurrentMapData = 0; + m_CurrentMapSize = 0; + } + + + int TrySetClientName(int ClientID, const char *pName) + { + int i; + char aTrimmedName[64]; + + /* trim the name */ + str_copy(aTrimmedName, str_ltrim(pName), sizeof(aTrimmedName)); + str_rtrim(aTrimmedName); + dbg_msg("", "'%s' -> '%s'", pName, aTrimmedName); + pName = aTrimmedName; + + + /* check for empty names */ + if(!pName[0]) + return -1; + + /* make sure that two clients doesn't have the same name */ + for(i = 0; i < MAX_CLIENTS; i++) + if(i != ClientID && m_aClients[i].m_State >= CClient::STATE_READY) + { + if(strcmp(pName, m_aClients[i].m_aName) == 0) + return -1; + } + + /* set the client name */ + str_copy(m_aClients[ClientID].m_aName, pName, MAX_NAME_LENGTH); + return 0; + } + + + + void SetClientName(int ClientID, const char *pName) + { + if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) + return; + + if(!pName) + return; + + char aNameTry[MAX_NAME_LENGTH]; + str_copy(aNameTry, pName, MAX_NAME_LENGTH); + if(TrySetClientName(ClientID, aNameTry)) + { + /* auto rename */ + for(int i = 1;; i++) + { + str_format(aNameTry, MAX_NAME_LENGTH, "(%d)%s", i, pName); + if(TrySetClientName(ClientID, aNameTry) == 0) + break; + } + } + } + + void SetClientScore(int ClientID, int Score) + { + if(ClientID < 0 || ClientID > MAX_CLIENTS || m_aClients[ClientID].m_State < CClient::STATE_READY) + return; + m_aClients[ClientID].m_Score = Score; + } + + void SetBrowseInfo(const char *pGameType, int Progression) + { + str_copy(m_aBrowseinfoGametype, pGameType, sizeof(m_aBrowseinfoGametype)); + m_BrowseinfoProgression = Progression; + if(m_BrowseinfoProgression > 100) + m_BrowseinfoProgression = 100; + if(m_BrowseinfoProgression < -1) + m_BrowseinfoProgression = -1; + } + + void Kick(int ClientID, const char *pReason) + { + if(ClientID < 0 || ClientID > MAX_CLIENTS) + return; + + if(m_aClients[ClientID].m_State != CClient::STATE_EMPTY) + m_NetServer.Drop(ClientID, pReason); + } + + int Tick() + { + return m_CurrentTick; + } + + int64 TickStartTime(int Tick) + { + return m_GameStartTime + (time_freq()*Tick)/SERVER_TICK_SPEED; + } + + int TickSpeed() + { + return SERVER_TICK_SPEED; + } + + int Init() + { + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + m_aClients[i].m_State = CClient::STATE_EMPTY; + m_aClients[i].m_aName[0] = 0; + m_aClients[i].m_aClan[0] = 0; + m_aClients[i].m_Snapshots.Init(); + } + + m_CurrentTick = 0; + + return 0; + } + + int GetClientInfo(int ClientID, CLIENT_INFO *pInfo) + { + dbg_assert(ClientID >= 0 && ClientID < MAX_CLIENTS, "client_id is not valid"); + dbg_assert(pInfo != 0, "info can not be null"); + + if(m_aClients[ClientID].m_State == CClient::STATE_INGAME) + { + pInfo->name = m_aClients[ClientID].m_aName; + pInfo->latency = m_aClients[ClientID].m_Latency; + return 1; + } + return 0; + } + + int SendMsg(int ClientID) + { + const MSG_INFO *pInfo = msg_get_info(); + CNetChunk Packet; + if(!pInfo) + return -1; + + mem_zero(&Packet, sizeof(CNetChunk)); + + Packet.m_ClientID = ClientID; + Packet.m_pData = pInfo->data; + Packet.m_DataSize = pInfo->size; + + if(pInfo->flags&MSGFLAG_VITAL) + Packet.m_Flags |= NETSENDFLAG_VITAL; + if(pInfo->flags&MSGFLAG_FLUSH) + Packet.m_Flags |= NETSENDFLAG_FLUSH; + + /* write message to demo recorder */ + if(!(pInfo->flags&MSGFLAG_NORECORD)) + demorec_record_message(pInfo->data, pInfo->size); + + if(!(pInfo->flags&MSGFLAG_NOSEND)) + { + if(ClientID == -1) + { + /* broadcast */ + int i; + for(i = 0; i < MAX_CLIENTS; i++) + if(m_aClients[i].m_State == CClient::STATE_INGAME) + { + Packet.m_ClientID = i; + m_NetServer.Send(&Packet); + } + } + else + m_NetServer.Send(&Packet); + } + return 0; + } + + void DoSnapshot() + { + int i; + + { + static PERFORMACE_INFO scope = {"presnap", 0}; + perf_start(&scope); + mods_presnap(); + perf_end(); + } + + /* create snapshot for demo recording */ + if(demorec_isrecording()) + { + char data[CSnapshot::MAX_SIZE]; + int snapshot_size; + + /* build snap and possibly add some messages */ + m_SnapshotBuilder.Init(); + mods_snap(-1); + snapshot_size = m_SnapshotBuilder.Finish(data); + + /* write snapshot */ + demorec_record_snapshot(Tick(), data, snapshot_size); + } + + /* create snapshots for all clients */ + for(i = 0; i < MAX_CLIENTS; i++) + { + /* client must be ingame to recive snapshots */ + if(m_aClients[i].m_State != CClient::STATE_INGAME) + continue; + + /* this client is trying to recover, don't spam snapshots */ + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_RECOVER && (Tick()%50) != 0) + continue; + + /* this client is trying to recover, don't spam snapshots */ + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_INIT && (Tick()%10) != 0) + continue; + + { + char data[CSnapshot::MAX_SIZE]; + char deltadata[CSnapshot::MAX_SIZE]; + char compdata[CSnapshot::MAX_SIZE]; + int snapshot_size; + int crc; + static CSnapshot emptysnap; + CSnapshot *deltashot = &emptysnap; + int deltashot_size; + int delta_tick = -1; + int deltasize; + static PERFORMACE_INFO scope = {"build", 0}; + perf_start(&scope); + + m_SnapshotBuilder.Init(); + + { + static PERFORMACE_INFO scope = {"modsnap", 0}; + perf_start(&scope); + mods_snap(i); + perf_end(); + } + + /* finish snapshot */ + snapshot_size = m_SnapshotBuilder.Finish(data); + crc = ((CSnapshot*)data)->Crc(); + + /* remove old snapshos */ + /* keep 3 seconds worth of snapshots */ + m_aClients[i].m_Snapshots.PurgeUntil(m_CurrentTick-SERVER_TICK_SPEED*3); + + /* save it the snapshot */ + m_aClients[i].m_Snapshots.Add(m_CurrentTick, time_get(), snapshot_size, data, 0); + + /* find snapshot that we can preform delta against */ + emptysnap.m_DataSize = 0; + emptysnap.m_NumItems = 0; + + { + deltashot_size = m_aClients[i].m_Snapshots.Get(m_aClients[i].m_LastAckedSnapshot, 0, &deltashot, 0); + if(deltashot_size >= 0) + delta_tick = m_aClients[i].m_LastAckedSnapshot; + else + { + /* no acked package found, force client to recover rate */ + if(m_aClients[i].m_SnapRate == CClient::SNAPRATE_FULL) + m_aClients[i].m_SnapRate = CClient::SNAPRATE_RECOVER; + } + } + + /* create delta */ + { + static PERFORMACE_INFO scope = {"delta", 0}; + perf_start(&scope); + deltasize = CSnapshot::CreateDelta(deltashot, (CSnapshot*)data, deltadata); + perf_end(); + } + + + if(deltasize) + { + /* compress it */ + int snapshot_size; + const int max_size = MAX_SNAPSHOT_PACKSIZE; + int numpackets; + int n, left; + + { + static PERFORMACE_INFO scope = {"compress", 0}; + /*char buffer[512];*/ + perf_start(&scope); + snapshot_size = intpack_compress(deltadata, deltasize, compdata); + + /*str_hex(buffer, sizeof(buffer), compdata, snapshot_size); + dbg_msg("", "deltasize=%d -> %d : %s", deltasize, snapshot_size, buffer);*/ + + perf_end(); + } + + numpackets = (snapshot_size+max_size-1)/max_size; + + for(n = 0, left = snapshot_size; left; n++) + { + int chunk = left < max_size ? left : max_size; + left -= chunk; + + if(numpackets == 1) + msg_pack_start_system(NETMSG_SNAPSINGLE, MSGFLAG_FLUSH); + else + msg_pack_start_system(NETMSG_SNAP, MSGFLAG_FLUSH); + + msg_pack_int(m_CurrentTick); + msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */ + + if(numpackets != 1) + { + msg_pack_int(numpackets); + msg_pack_int(n); + } + + msg_pack_int(crc); + msg_pack_int(chunk); + msg_pack_raw(&compdata[n*max_size], chunk); + msg_pack_end(); + SendMsg(i); + } + } + else + { + msg_pack_start_system(NETMSG_SNAPEMPTY, MSGFLAG_FLUSH); + msg_pack_int(m_CurrentTick); + msg_pack_int(m_CurrentTick-delta_tick); /* compressed with */ + msg_pack_end(); + SendMsg(i); + } + + perf_end(); + } + } + + mods_postsnap(); + } + + + static int NewClientCallback(int cid, void *pUser) + { + CServer *pThis = (CServer *)pUser; + pThis->m_aClients[cid].m_State = CClient::STATE_AUTH; + pThis->m_aClients[cid].m_aName[0] = 0; + pThis->m_aClients[cid].m_aClan[0] = 0; + pThis->m_aClients[cid].m_Authed = 0; + pThis->m_aClients[cid].Reset(); + return 0; + } + + static int DelClientCallback(int cid, void *pUser) + { + CServer *pThis = (CServer *)pUser; + + /* notify the mod about the drop */ + if(pThis->m_aClients[cid].m_State >= CClient::STATE_READY) + mods_client_drop(cid); + + pThis->m_aClients[cid].m_State = CClient::STATE_EMPTY; + pThis->m_aClients[cid].m_aName[0] = 0; + pThis->m_aClients[cid].m_aClan[0] = 0; + pThis->m_aClients[cid].m_Authed = 0; + pThis->m_aClients[cid].m_Snapshots.PurgeAll(); + return 0; + } + + void SendMap(int cid) + { + msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_string(config.sv_map, 0); + msg_pack_int(m_CurrentMapCrc); + msg_pack_end(); + server_send_msg(cid); + } + + void SendRconLine(int cid, const char *pLine) + { + msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); + msg_pack_string(pLine, 512); + msg_pack_end(); + server_send_msg(cid); + } + + static void SendRconLineAuthed(const char *pLine, void *pUser) + { + CServer *pThis = (CServer *)pUser; + static volatile int reentry_guard = 0; + int i; + + if(reentry_guard) return; + reentry_guard++; + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed) + pThis->SendRconLine(i, pLine); + } + + reentry_guard--; + } + + void ProcessClientPacket(CNetChunk *pPacket) + { + int cid = pPacket->m_ClientID; + NETADDR addr; + + int sys; + int msg = msg_unpack_start(pPacket->m_pData, pPacket->m_DataSize, &sys); + + if(m_aClients[cid].m_State == CClient::STATE_AUTH) + { + if(sys && msg == NETMSG_INFO) + { + char version[64]; + const char *password; + str_copy(version, msg_unpack_string(), 64); + if(strcmp(version, mods_net_version()) != 0) + { + /* OH FUCK! wrong version, drop him */ + char reason[256]; + str_format(reason, sizeof(reason), "wrong version. server is running '%s' and client '%s'.", mods_net_version(), version); + m_NetServer.Drop(cid, reason); + return; + } + + str_copy(m_aClients[cid].m_aName, msg_unpack_string(), MAX_NAME_LENGTH); + str_copy(m_aClients[cid].m_aClan, msg_unpack_string(), MAX_CLANNAME_LENGTH); + password = msg_unpack_string(); + + if(config.password[0] != 0 && strcmp(config.password, password) != 0) + { + /* wrong password */ + m_NetServer.Drop(cid, "wrong password"); + return; + } + + m_aClients[cid].m_State = CClient::STATE_CONNECTING; + SendMap(cid); + } + } + else + { + if(sys) + { + /* system message */ + if(msg == NETMSG_REQUEST_MAP_DATA) + { + int chunk = msg_unpack_int(); + int chunk_size = 1024-128; + int offset = chunk * chunk_size; + int last = 0; + + /* drop faulty map data requests */ + if(chunk < 0 || offset > m_CurrentMapSize) + return; + + if(offset+chunk_size >= m_CurrentMapSize) + { + chunk_size = m_CurrentMapSize-offset; + if(chunk_size < 0) + chunk_size = 0; + last = 1; + } + + msg_pack_start_system(NETMSG_MAP_DATA, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_int(last); + msg_pack_int(m_CurrentMapSize); + msg_pack_int(chunk_size); + msg_pack_raw(&m_pCurrentMapData[offset], chunk_size); + msg_pack_end(); + server_send_msg(cid); + + if(config.debug) + dbg_msg("server", "sending chunk %d with size %d", chunk, chunk_size); + } + else if(msg == NETMSG_READY) + { + if(m_aClients[cid].m_State == CClient::STATE_CONNECTING) + { + addr = m_NetServer.ClientAddr(cid); + + dbg_msg("server", "player is ready. cid=%x ip=%d.%d.%d.%d", + cid, + addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] + ); + m_aClients[cid].m_State = CClient::STATE_READY; + mods_connected(cid); + } + } + else if(msg == NETMSG_ENTERGAME) + { + if(m_aClients[cid].m_State == CClient::STATE_READY) + { + addr = m_NetServer.ClientAddr(cid); + + dbg_msg("server", "player has entered the game. cid=%x ip=%d.%d.%d.%d", + cid, + addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3] + ); + m_aClients[cid].m_State = CClient::STATE_INGAME; + mods_client_enter(cid); + } + } + else if(msg == NETMSG_INPUT) + { + int tick, size, i; + CClient::CInput *pInput; + int64 tagtime; + + m_aClients[cid].m_LastAckedSnapshot = msg_unpack_int(); + tick = msg_unpack_int(); + size = msg_unpack_int(); + + /* check for errors */ + if(msg_unpack_error() || size/4 > MAX_INPUT_SIZE) + return; + + if(m_aClients[cid].m_LastAckedSnapshot > 0) + m_aClients[cid].m_SnapRate = CClient::SNAPRATE_FULL; + + if(m_aClients[cid].m_Snapshots.Get(m_aClients[cid].m_LastAckedSnapshot, &tagtime, 0, 0) >= 0) + m_aClients[cid].m_Latency = (int)(((time_get()-tagtime)*1000)/time_freq()); + + /* add message to report the input timing */ + /* skip packets that are old */ + if(tick > m_aClients[cid].m_LastInputTick) + { + int time_left = ((TickStartTime(tick)-time_get())*1000) / time_freq(); + msg_pack_start_system(NETMSG_INPUTTIMING, 0); + msg_pack_int(tick); + msg_pack_int(time_left); + msg_pack_end(); + server_send_msg(cid); + } + + m_aClients[cid].m_LastInputTick = tick; + + pInput = &m_aClients[cid].m_aInputs[m_aClients[cid].m_CurrentInput]; + + if(tick <= server_tick()) + tick = server_tick()+1; + + pInput->m_GameTick = tick; + + for(i = 0; i < size/4; i++) + pInput->m_aData[i] = msg_unpack_int(); + + mem_copy(m_aClients[cid].m_LatestInput.m_aData, pInput->m_aData, MAX_INPUT_SIZE*sizeof(int)); + + m_aClients[cid].m_CurrentInput++; + m_aClients[cid].m_CurrentInput %= 200; + + /* call the mod with the fresh input data */ + if(m_aClients[cid].m_State == CClient::STATE_INGAME) + mods_client_direct_input(cid, m_aClients[cid].m_LatestInput.m_aData); + } + else if(msg == NETMSG_RCON_CMD) + { + const char *cmd = msg_unpack_string(); + + if(msg_unpack_error() == 0 && m_aClients[cid].m_Authed) + { + dbg_msg("server", "cid=%d rcon='%s'", cid, cmd); + console_execute_line(cmd); + } + } + else if(msg == NETMSG_RCON_AUTH) + { + const char *pw; + msg_unpack_string(); /* login name, not used */ + pw = msg_unpack_string(); + + if(msg_unpack_error() == 0) + { + if(config.sv_rcon_password[0] == 0) + { + SendRconLine(cid, "No rcon password set on server. Set sv_rcon_password to enable the remote console."); + } + else if(strcmp(pw, config.sv_rcon_password) == 0) + { + msg_pack_start_system(NETMSG_RCON_AUTH_STATUS, MSGFLAG_VITAL); + msg_pack_int(1); + msg_pack_end(); + server_send_msg(cid); + + m_aClients[cid].m_Authed = 1; + SendRconLine(cid, "Authentication successful. Remote console access granted."); + dbg_msg("server", "cid=%d authed", cid); + } + else + { + SendRconLine(cid, "Wrong password."); + } + } + } + else if(msg == NETMSG_PING) + { + msg_pack_start_system(NETMSG_PING_REPLY, 0); + msg_pack_end(); + server_send_msg(cid); + } + else + { + char hex[] = "0123456789ABCDEF"; + char buf[512]; + int b; + + for(b = 0; b < pPacket->m_DataSize && b < 32; b++) + { + buf[b*3] = hex[((const unsigned char *)pPacket->m_pData)[b]>>4]; + buf[b*3+1] = hex[((const unsigned char *)pPacket->m_pData)[b]&0xf]; + buf[b*3+2] = ' '; + buf[b*3+3] = 0; + } + + dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, pPacket->m_DataSize); + dbg_msg("server", "%s", buf); + + } + } + else + { + /* game message */ + if(m_aClients[cid].m_State >= CClient::STATE_READY) + mods_message(msg, cid); + } + } + } + + void SendServerInfo(NETADDR *addr, int token) + { + CNetChunk Packet; + CPacker p; + char buf[128]; + + /* count the players */ + int player_count = 0; + int i; + for(i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + player_count++; + } + + p.Reset(); + + if(token >= 0) + { + /* new token based format */ + p.AddRaw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + str_format(buf, sizeof(buf), "%d", token); + p.AddString(buf, 6); + } + else + { + /* old format */ + p.AddRaw(SERVERBROWSE_OLD_INFO, sizeof(SERVERBROWSE_OLD_INFO)); + } + + p.AddString(mods_version(), 32); + p.AddString(config.sv_name, 64); + p.AddString(config.sv_map, 32); + + /* gametype */ + p.AddString(m_aBrowseinfoGametype, 16); + + /* flags */ + i = 0; + if(config.password[0]) /* password set */ + i |= SRVFLAG_PASSWORD; + str_format(buf, sizeof(buf), "%d", i); + p.AddString(buf, 2); + + /* progression */ + str_format(buf, sizeof(buf), "%d", m_BrowseinfoProgression); + p.AddString(buf, 4); + + str_format(buf, sizeof(buf), "%d", player_count); p.AddString(buf, 3); /* num players */ + str_format(buf, sizeof(buf), "%d", m_NetServer.MaxClients()); p.AddString(buf, 3); /* max players */ + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].m_State != CClient::STATE_EMPTY) + { + p.AddString(m_aClients[i].m_aName, 48); /* player name */ + str_format(buf, sizeof(buf), "%d", m_aClients[i].m_Score); p.AddString(buf, 6); /* player score */ + } + } + + + Packet.m_ClientID = -1; + Packet.m_Address = *addr; + Packet.m_Flags = NETSENDFLAG_CONNLESS; + Packet.m_DataSize = p.Size(); + Packet.m_pData = p.Data(); + m_NetServer.Send(&Packet); + } + + int BanAdd(NETADDR Addr, int Seconds) + { + return m_NetServer.BanAdd(Addr, Seconds); + } + + int BanRemove(NETADDR Addr) + { + return m_NetServer.BanRemove(Addr); + } + + + void PumpNetwork() + { + CNetChunk Packet; + + m_NetServer.Update(); + + /* process packets */ + while(m_NetServer.Recv(&Packet)) + { + if(Packet.m_ClientID == -1) + { + /* stateless */ + if(!register_process_packet(&Packet)) + { + if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && + memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + SendServerInfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); + } + + + if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && + memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) + { + SendServerInfo(&Packet.m_Address, -1); + } + } + } + else + ProcessClientPacket(&Packet); + } + } + + int LoadMap(const char *pMapName) + { + DATAFILE *df; + char buf[512]; + str_format(buf, sizeof(buf), "maps/%s.map", pMapName); + df = datafile_load(buf); + if(!df) + return 0; + + /* stop recording when we change map */ + demorec_record_stop(); + + /* reinit snapshot ids */ + m_IDPool.TimeoutIDs(); + + /* get the crc of the map */ + m_CurrentMapCrc = datafile_crc(buf); + dbg_msg("server", "%s crc is %08x", buf, m_CurrentMapCrc); + + str_copy(m_aCurrentMap, pMapName, sizeof(m_aCurrentMap)); + map_set(df); + + /* load compelate map into memory for download */ + { + IOHANDLE file = engine_openfile(buf, IOFLAG_READ); + m_CurrentMapSize = (int)io_length(file); + if(m_pCurrentMapData) + mem_free(m_pCurrentMapData); + m_pCurrentMapData = (unsigned char *)mem_alloc(m_CurrentMapSize, 1); + io_read(file, m_pCurrentMapData, m_CurrentMapSize); + io_close(file); + } + return 1; + } + + int Run() + { + NETADDR bindaddr; + + //snap_init_id(); + net_init(); + + /* */ + console_register_print_callback(SendRconLineAuthed, this); + + /* load map */ + if(!LoadMap(config.sv_map)) + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + return -1; + } + + /* start server */ + /* TODO: IPv6 support */ + if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) + { + /* sweet! */ + bindaddr.port = config.sv_port; + } + else + { + mem_zero(&bindaddr, sizeof(bindaddr)); + bindaddr.port = config.sv_port; + } + + + if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0)) + { + dbg_msg("server", "couldn't open socket. port might already be in use"); + return -1; + } + + m_NetServer.SetCallbacks(NewClientCallback, DelClientCallback, this); + + dbg_msg("server", "server name is '%s'", config.sv_name); + + mods_init(); + dbg_msg("server", "version %s", mods_net_version()); + + /* start game */ + { + int64 reporttime = time_get(); + int reportinterval = 3; + + m_Lastheartbeat = 0; + m_GameStartTime = time_get(); + + if(config.debug) + dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); + + while(m_RunServer) + { + static PERFORMACE_INFO rootscope = {"root", 0}; + int64 t = time_get(); + int new_ticks = 0; + + perf_start(&rootscope); + + /* load new map TODO: don't poll this */ + if(strcmp(config.sv_map, m_aCurrentMap) != 0 || config.sv_map_reload) + { + config.sv_map_reload = 0; + + /* load map */ + if(LoadMap(config.sv_map)) + { + int c; + + /* new map loaded */ + mods_shutdown(); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].m_State == CClient::STATE_EMPTY) + continue; + + SendMap(c); + m_aClients[c].Reset(); + m_aClients[c].m_State = CClient::STATE_CONNECTING; + } + + m_GameStartTime = time_get(); + m_CurrentTick = 0; + mods_init(); + } + else + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + config_set_sv_map(&config, m_aCurrentMap); + } + } + + while(t > TickStartTime(m_CurrentTick+1)) + { + m_CurrentTick++; + new_ticks++; + + /* apply new input */ + { + static PERFORMACE_INFO scope = {"input", 0}; + int c, i; + + perf_start(&scope); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].m_State == CClient::STATE_EMPTY) + continue; + for(i = 0; i < 200; i++) + { + if(m_aClients[c].m_aInputs[i].m_GameTick == server_tick()) + { + if(m_aClients[c].m_State == CClient::STATE_INGAME) + mods_client_predicted_input(c, m_aClients[c].m_aInputs[i].m_aData); + break; + } + } + } + + perf_end(); + } + + /* progress game */ + { + static PERFORMACE_INFO scope = {"tick", 0}; + perf_start(&scope); + mods_tick(); + perf_end(); + } + } + + /* snap game */ + if(new_ticks) + { + if(config.sv_high_bandwidth || (m_CurrentTick%2) == 0) + { + static PERFORMACE_INFO scope = {"snap", 0}; + perf_start(&scope); + DoSnapshot(); + perf_end(); + } + } + + /* master server stuff */ + register_update(); + + + { + static PERFORMACE_INFO scope = {"net", 0}; + perf_start(&scope); + PumpNetwork(); + perf_end(); + } + + perf_end(); + + if(reporttime < time_get()) + { + if(config.debug) + { + /* + static NETSTATS prev_stats; + NETSTATS stats; + netserver_stats(net, &stats); + + perf_next(); + + if(config.dbg_pref) + perf_dump(&rootscope); + + dbg_msg("server", "send=%8d recv=%8d", + (stats.send_bytes - prev_stats.send_bytes)/reportinterval, + (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); + + prev_stats = stats; + */ + } + + reporttime += time_freq()*reportinterval; + } + + /* wait for incomming data */ + net_socket_read_wait(m_NetServer.Socket(), 5); + } + } + + mods_shutdown(); + map_unload(); + + if(m_pCurrentMapData) + mem_free(m_pCurrentMapData); + return 0; + } + + static void ConKick(void *pResult, void *pUser) + { + ((CServer *)pUser)->Kick(console_arg_int(pResult, 0), "kicked by console"); + } + + static int str_allnum(const char *str) + { + while(*str) + { + if(!(*str >= '0' && *str <= '9')) + return 0; + str++; + } + return 1; + } + + static void ConBan(void *pResult, void *pUser) + { + NETADDR addr; + char addrstr[128]; + const char *str = console_arg_string(pResult, 0); + int minutes = 30; + + if(console_arg_num(pResult) > 1) + minutes = console_arg_int(pResult, 1); + + if(net_addr_from_str(&addr, str) == 0) + ((CServer *)pUser)->BanAdd(addr, minutes*60); + else if(str_allnum(str)) + { + NETADDR addr; + int cid = atoi(str); + + if(cid < 0 || cid > MAX_CLIENTS || ((CServer *)pUser)->m_aClients[cid].m_State == CClient::STATE_EMPTY) + { + dbg_msg("server", "invalid client id"); + return; + } + + addr = ((CServer *)pUser)->m_NetServer.ClientAddr(cid); + ((CServer *)pUser)->BanAdd(addr, minutes*60); + } + + addr.port = 0; + net_addr_str(&addr, addrstr, sizeof(addrstr)); + + if(minutes) + dbg_msg("server", "banned %s for %d minutes", addrstr, minutes); + else + dbg_msg("server", "banned %s for life", addrstr); + } + + static void ConUnban(void *result, void *pUser) + { + NETADDR addr; + const char *str = console_arg_string(result, 0); + + if(net_addr_from_str(&addr, str) == 0) + ((CServer *)pUser)->BanRemove(addr); + else + dbg_msg("server", "invalid network address"); + } + + static void ConBans(void *pResult, void *pUser) + { + unsigned now = time_timestamp(); + + int num = ((CServer *)pUser)->m_NetServer.BanNum(); + for(int i = 0; i < num; i++) + { + CNetServer::CBanInfo Info; + ((CServer *)pUser)->m_NetServer.BanGet(i, &Info); + NETADDR Addr = Info.m_Addr; + + if(Info.m_Expires == -1) + { + dbg_msg("server", "#%d %d.%d.%d.%d for life", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3]); + } + else + { + unsigned t = Info.m_Expires - now; + dbg_msg("server", "#%d %d.%d.%d.%d for %d minutes and %d seconds", i, Addr.ip[0], Addr.ip[1], Addr.ip[2], Addr.ip[3], t/60, t%60); + } + } + dbg_msg("server", "%d ban(s)", num); + } + + static void ConStatus(void *pResult, void *pUser) + { + int i; + NETADDR addr; + for(i = 0; i < MAX_CLIENTS; i++) + { + if(((CServer *)pUser)->m_aClients[i].m_State == CClient::STATE_INGAME) + { + addr = ((CServer *)pUser)->m_NetServer.ClientAddr(i); + dbg_msg("server", "id=%d addr=%d.%d.%d.%d:%d name='%s' score=%d", + i, addr.ip[0], addr.ip[1], addr.ip[2], addr.ip[3], addr.port, + ((CServer *)pUser)->m_aClients[i].m_aName, ((CServer *)pUser)->m_aClients[i].m_Score); + } + } + } + + static void ConShutdown(void *pResult, void *pUser) + { + ((CServer *)pUser)->m_RunServer = 0; + } + + static void ConRecord(void *pResult, void *pUser) + { + char filename[512]; + str_format(filename, sizeof(filename), "demos/%s.demo", console_arg_string(pResult, 0)); + demorec_record_start(filename, mods_net_version(), ((CServer *)pUser)->m_aCurrentMap, ((CServer *)pUser)->m_CurrentMapCrc, "server"); + } + + static void ConStopRecord(void *pResult, void *pUser) + { + demorec_record_stop(); + } + + + void RegisterCommands() + { + MACRO_REGISTER_COMMAND("kick", "i", CFGFLAG_SERVER, ConKick, this, ""); + MACRO_REGISTER_COMMAND("ban", "s?i", CFGFLAG_SERVER, ConBan, this, ""); + MACRO_REGISTER_COMMAND("unban", "s", CFGFLAG_SERVER, ConUnban, this, ""); + MACRO_REGISTER_COMMAND("bans", "", CFGFLAG_SERVER, ConBans, this, ""); + MACRO_REGISTER_COMMAND("status", "", CFGFLAG_SERVER, ConStatus, this, ""); + MACRO_REGISTER_COMMAND("shutdown", "", CFGFLAG_SERVER, ConShutdown, this, ""); + + MACRO_REGISTER_COMMAND("record", "s", CFGFLAG_SERVER, ConRecord, this, ""); + MACRO_REGISTER_COMMAND("stoprecord", "", CFGFLAG_SERVER, ConStopRecord, this, ""); + } + +}; + + +// UGLY UGLY HACK for now +CServer g_Server; +CNetServer *m_pNetServer; + +int server_tick() { return g_Server.Tick(); } +int server_tickspeed() { return g_Server.TickSpeed(); } +int snap_new_id() { return g_Server.m_IDPool.NewID(); } +void snap_free_id(int id) { return g_Server.m_IDPool.FreeID(id); } +int server_send_msg(int client_id) { return g_Server.SendMsg(client_id); } +void server_setbrowseinfo(const char *game_type, int progression) { g_Server.SetBrowseInfo(game_type, progression); } +void server_setclientname(int client_id, const char *name) { g_Server.SetClientName(client_id, name); } +void server_setclientscore(int client_id, int score) { g_Server.SetClientScore(client_id, score); } + +int server_getclientinfo(int client_id, CLIENT_INFO *info) { return g_Server.GetClientInfo(client_id, info); } + +void *snap_new_item(int type, int id, int size) +{ + dbg_assert(type >= 0 && type <=0xffff, "incorrect type"); + dbg_assert(id >= 0 && id <=0xffff, "incorrect id"); + return g_Server.m_SnapshotBuilder.NewItem(type, id, size); +} + +int *server_latestinput(int client_id, int *size) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY) + return 0; + return g_Server.m_aClients[client_id].m_LatestInput.m_aData; +} + +const char *server_clientname(int client_id) +{ + if(client_id < 0 || client_id > MAX_CLIENTS || g_Server.m_aClients[client_id].m_State < CServer::CClient::STATE_READY) + return "(invalid client)"; + return g_Server.m_aClients[client_id].m_aName; +} + +#if 0 + + +static void reset_client(int cid) +{ + /* reset input */ + int i; + for(i = 0; i < 200; i++) + m_aClients[cid].m_aInputs[i].m_GameTick = -1; + m_aClients[cid].m_CurrentInput = 0; + mem_zero(&m_aClients[cid].m_Latestinput, sizeof(m_aClients[cid].m_Latestinput)); + + m_aClients[cid].m_Snapshots.PurgeAll(); + m_aClients[cid].m_LastAckedSnapshot = -1; + m_aClients[cid].m_LastInputTick = -1; + m_aClients[cid].m_SnapRate = CClient::SNAPRATE_INIT; + m_aClients[cid].m_Score = 0; + +} + + +static int new_client_callback(int cid, void *user) +{ + m_aClients[cid].state = CClient::STATE_AUTH; + m_aClients[cid].name[0] = 0; + m_aClients[cid].clan[0] = 0; + m_aClients[cid].authed = 0; + reset_client(cid); + return 0; +} + +static int del_client_callback(int cid, void *user) +{ + /* notify the mod about the drop */ + if(m_aClients[cid].state >= CClient::STATE_READY) + mods_client_drop(cid); + + m_aClients[cid].state = CClient::STATE_EMPTY; + m_aClients[cid].name[0] = 0; + m_aClients[cid].clan[0] = 0; + m_aClients[cid].authed = 0; + m_aClients[cid].snapshots.PurgeAll(); + return 0; +} +static void server_send_map(int cid) +{ + msg_pack_start_system(NETMSG_MAP_CHANGE, MSGFLAG_VITAL|MSGFLAG_FLUSH); + msg_pack_string(config.sv_map, 0); + msg_pack_int(current_map_crc); + msg_pack_end(); + server_send_msg(cid); +} + +static void server_send_rcon_line(int cid, const char *line) +{ + msg_pack_start_system(NETMSG_RCON_LINE, MSGFLAG_VITAL); + msg_pack_string(line, 512); + msg_pack_end(); + server_send_msg(cid); +} + +static void server_send_rcon_line_authed(const char *line, void *user_data) +{ + static volatile int reentry_guard = 0; + int i; + + if(reentry_guard) return; + reentry_guard++; + + for(i = 0; i < MAX_CLIENTS; i++) + { + if(m_aClients[i].state != CClient::STATE_EMPTY && m_aClients[i].authed) + server_send_rcon_line(i, line); + } + + reentry_guard--; +} + +static void server_pump_network() +{ + CNetChunk Packet; + + m_NetServer.Update(); + + /* process packets */ + while(m_NetServer.Recv(&Packet)) + { + if(Packet.m_ClientID == -1) + { + /* stateless */ + if(!register_process_packet(&Packet)) + { + if(Packet.m_DataSize == sizeof(SERVERBROWSE_GETINFO)+1 && + memcmp(Packet.m_pData, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + server_send_serverinfo(&Packet.m_Address, ((unsigned char *)Packet.m_pData)[sizeof(SERVERBROWSE_GETINFO)]); + } + + + if(Packet.m_DataSize == sizeof(SERVERBROWSE_OLD_GETINFO) && + memcmp(Packet.m_pData, SERVERBROWSE_OLD_GETINFO, sizeof(SERVERBROWSE_OLD_GETINFO)) == 0) + { + server_send_serverinfo(&Packet.m_Address, -1); + } + } + } + else + server_process_client_packet(&Packet); + } +} + +static int server_load_map(const char *mapname) +{ + DATAFILE *df; + char buf[512]; + str_format(buf, sizeof(buf), "maps/%s.map", mapname); + df = datafile_load(buf); + if(!df) + return 0; + + /* stop recording when we change map */ + demorec_record_stop(); + + /* reinit snapshot ids */ + snap_timeout_ids(); + + /* get the crc of the map */ + current_map_crc = datafile_crc(buf); + dbg_msg("server", "%s crc is %08x", buf, current_map_crc); + + str_copy(current_map, mapname, sizeof(current_map)); + map_set(df); + + /* load compelate map into memory for download */ + { + IOHANDLE file = engine_openfile(buf, IOFLAG_READ); + current_map_size = (int)io_length(file); + if(current_map_data) + mem_free(current_map_data); + current_map_data = (unsigned char *)mem_alloc(current_map_size, 1); + io_read(file, current_map_data, current_map_size); + io_close(file); + } + return 1; +} + +static int server_run() +{ + NETADDR bindaddr; + + snap_init_id(); + net_init(); + + /* */ + console_register_print_callback(server_send_rcon_line_authed, 0); + + /* load map */ + if(!server_load_map(config.sv_map)) + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + return -1; + } + + /* start server */ + /* TODO: IPv6 support */ + if(config.sv_bindaddr[0] && net_host_lookup(config.sv_bindaddr, &bindaddr, NETTYPE_IPV4) == 0) + { + /* sweet! */ + bindaddr.port = config.sv_port; + } + else + { + mem_zero(&bindaddr, sizeof(bindaddr)); + bindaddr.port = config.sv_port; + } + + + if(!m_NetServer.Open(bindaddr, config.sv_max_clients, 0)) + { + dbg_msg("server", "couldn't open socket. port might already be in use"); + return -1; + } + + m_NetServer.SetCallbacks(new_client_callback, del_client_callback, this); + + dbg_msg("server", "server name is '%s'", config.sv_name); + + mods_init(); + dbg_msg("server", "version %s", mods_net_version()); + + /* start game */ + { + int64 reporttime = time_get(); + int reportinterval = 3; + + lastheartbeat = 0; + game_start_time = time_get(); + + if(config.debug) + dbg_msg("server", "baseline memory usage %dk", mem_stats()->allocated/1024); + + while(run_server) + { + static PERFORMACE_INFO rootscope = {"root", 0}; + int64 t = time_get(); + int new_ticks = 0; + + perf_start(&rootscope); + + /* load new map TODO: don't poll this */ + if(strcmp(config.sv_map, current_map) != 0 || config.sv_map_reload) + { + config.sv_map_reload = 0; + + /* load map */ + if(server_load_map(config.sv_map)) + { + int c; + + /* new map loaded */ + mods_shutdown(); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].state == CClient::STATE_EMPTY) + continue; + + server_send_map(c); + reset_client(c); + m_aClients[c].state = CClient::STATE_CONNECTING; + } + + game_start_time = time_get(); + current_tick = 0; + mods_init(); + } + else + { + dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map); + config_set_sv_map(&config, current_map); + } + } + + while(t > server_tick_start_time(current_tick+1)) + { + current_tick++; + new_ticks++; + + /* apply new input */ + { + static PERFORMACE_INFO scope = {"input", 0}; + int c, i; + + perf_start(&scope); + + for(c = 0; c < MAX_CLIENTS; c++) + { + if(m_aClients[c].state == CClient::STATE_EMPTY) + continue; + for(i = 0; i < 200; i++) + { + if(m_aClients[c].inputs[i].game_tick == server_tick()) + { + if(m_aClients[c].state == CClient::STATE_INGAME) + mods_client_predicted_input(c, m_aClients[c].inputs[i].data); + break; + } + } + } + + perf_end(); + } + + /* progress game */ + { + static PERFORMACE_INFO scope = {"tick", 0}; + perf_start(&scope); + mods_tick(); + perf_end(); + } + } + + /* snap game */ + if(new_ticks) + { + if(config.sv_high_bandwidth || (current_tick%2) == 0) + { + static PERFORMACE_INFO scope = {"snap", 0}; + perf_start(&scope); + server_do_snap(); + perf_end(); + } + } + + /* master server stuff */ + register_update(); + + + { + static PERFORMACE_INFO scope = {"net", 0}; + perf_start(&scope); + server_pump_network(); + perf_end(); + } + + perf_end(); + + if(reporttime < time_get()) + { + if(config.debug) + { + /* + static NETSTATS prev_stats; + NETSTATS stats; + netserver_stats(net, &stats); + + perf_next(); + + if(config.dbg_pref) + perf_dump(&rootscope); + + dbg_msg("server", "send=%8d recv=%8d", + (stats.send_bytes - prev_stats.send_bytes)/reportinterval, + (stats.recv_bytes - prev_stats.recv_bytes)/reportinterval); + + prev_stats = stats; + */ + } + + reporttime += time_freq()*reportinterval; + } + + /* wait for incomming data */ + net_socket_read_wait(m_NetServer.Socket(), 5); + } + } + + mods_shutdown(); + map_unload(); + + if(current_map_data) + mem_free(current_map_data); + return 0; +} + + +#endif + +int main(int argc, char **argv) +{ + + m_pNetServer = &g_Server.m_NetServer; +#if defined(CONF_FAMILY_WINDOWS) + int i; + for(i = 1; i < argc; i++) + { + if(strcmp("-s", argv[i]) == 0 || strcmp("--silent", argv[i]) == 0) + { + ShowWindow(GetConsoleWindow(), SW_HIDE); + break; + } + } +#endif + + /* init the engine */ + dbg_msg("server", "starting..."); + engine_init("Teeworlds"); + + /* register all console commands */ + + g_Server.RegisterCommands(); + mods_console_init(); + + /* parse the command line arguments */ + engine_parse_arguments(argc, argv); + + /* run the server */ + g_Server.Run(); + return 0; +} + |