diff --git a/datasrc/teewars.ds b/datasrc/teewars.ds index 9923a5128..8c6deb12c 100644 --- a/datasrc/teewars.ds +++ b/datasrc/teewars.ds @@ -238,6 +238,16 @@ weapons { reloadtime 100 visual_size 96 } + + rocket_backpack { + sprite_body sprites.weapons.weapon_hammer_body + sprite_cursor sprites.weapons.weapon_hammer_cursor + sprite_proj sprites.weapons.weapon_hammer_proj + + recoil 10 + reloadtime 100 + visual_size 64 + } } sprites { diff --git a/default.bam b/default.bam index 2f375ac3f..839c2cbaf 100644 --- a/default.bam +++ b/default.bam @@ -91,43 +91,57 @@ function DataCompile(datafile, scriptfile, headerfile, sourcefile, outputdatafil return {data = outputdatafile, header=headerfile, source=sourcefile} end ---baselib = Import("src/baselib/baselib.bam") -baselib = Import("../baselib/baselib.bam") +config_name = "debug" +config_ext = "" + settings = NewSettings() +settings.cc.output = function(input, extention) + return Path("objs/" .. PathFilename(input) .. config_ext .. extention) +end + +baselib_options = {} +baselib_options.settings = settings +baselib = Import("../baselib/baselib.bam", baselib_options) baselib.apply(settings, "all") server_settings = NewSettings() baselib.apply(server_settings, "network") -settings.cc.debug = 1 -settings.cc.optimize = 0 if family == "windows" then settings.cc.flags = "/wd4244" else settings.cc.flags = "-Wall" end + settings.cc.includes:add("src") settings.cc.includes:add("../baselib/src/external/zlib") -serverdata = DataCompile("datasrc/teewars.ds", "datasrc/server.dts", "src/game/server/data.h", "src/game/server/data/data.cpp", "data/server.dat") -clientdata = DataCompile("datasrc/teewars.ds", "datasrc/client.dts", "src/game/client/data.h", "src/game/client/data/data.cpp", "data/client.dat") +serverdata = DataCompile("datasrc/teewars.ds", "datasrc/server.dts", "src/game/server/data.h", "src/game/server/data/server_data.cpp", "data/server.dat") +clientdata = DataCompile("datasrc/teewars.ds", "datasrc/client.dts", "src/game/client/data.h", "src/game/client/data/client_data.cpp", "data/client.dat") -engine = Compile(settings, Collect("src/engine/*.cpp")) -client = Compile(settings, Collect("src/engine/client/*.cpp", "src/engine/client/pnglite/*.c")) -server = Compile(settings, Collect("src/engine/server/*.cpp")) -game_shared = Compile(settings, Collect("src/game/*.cpp")) -game_client = Compile(settings, Collect("src/game/client/*.cpp"), clientdata.source) -game_server = Compile(settings, Collect("src/game/server/*.cpp"), serverdata.source) -editor = Compile(settings, Collect("src/editor/*.cpp")) +function build(config) + engine = Compile(settings, Collect("src/engine/*.cpp")) + client = Compile(settings, Collect("src/engine/client/*.cpp", "src/engine/client/pnglite/*.c")) + server = Compile(settings, Collect("src/engine/server/*.cpp")) + masterserver = Compile(settings, Collect("src/mastersrv/*.cpp")) + game_shared = Compile(settings, Collect("src/game/*.cpp")) + game_client = Compile(settings, Collect("src/game/client/*.cpp"), clientdata.source) + game_server = Compile(settings, Collect("src/game/server/*.cpp"), serverdata.source) + editor = Compile(settings, Collect("src/editor/*.cpp")) -crapnet = Compile(settings, Collect("src/crapnet/*.cpp")) + crapnet = Compile(settings, Collect("src/crapnet/*.cpp")) -client_exe = Link(settings, "teewars", engine, client, editor, game_shared, game_client) -server_exe = Link(server_settings, "teewars_srv", engine, server, game_shared, game_server) --- editor_exe = Link(settings, "editor", engine, game_shared, editor) -crapnet_exe = Link(server_settings, "crapnet", crapnet) + client_exe = Link(settings, "teewars"..config_ext, engine, client, editor, game_shared, game_client) + server_exe = Link(server_settings, "teewars_srv"..config_ext, engine, server, game_shared, game_server) + masterserver_exe = Link(server_settings, "mastersrv"..config_ext, masterserver, engine) + -- editor_exe = Link(settings, "editor", engine, game_shared, editor) + crapnet_exe = Link(server_settings, "crapnet"..config_ext, crapnet) -Target(PseudoTarget("client", client_exe, clientdata.data)) -Target(PseudoTarget("server", server_exe, serverdata.data)) -Target(PseudoTarget("tools", crapnet_exe)) --- Target(PseudoTarget("editor", editor_exe)) + Target(PseudoTarget("client", client_exe, clientdata.data)) + Target(PseudoTarget("server", server_exe, serverdata.data)) + Target(PseudoTarget("masterserver", masterserver_exe)) + Target(PseudoTarget("tools", crapnet_exe)) + -- Target(PseudoTarget("editor", editor_exe)) +end + +build("debug") diff --git a/masterserver/default.bam b/masterserver/default.bam deleted file mode 100644 index 06492b703..000000000 --- a/masterserver/default.bam +++ /dev/null @@ -1,18 +0,0 @@ -baselib = Import("../../baselib/baselib.bam") ---scheme = Import("scheme/scheme.bam") - -settings = NewSettings() -baselib.use(settings) ---scheme.use(settings) -settings.cc.includes:add("include") ---settings.cc.includes:add("include/python") ---settings.cc.frameworks:add("Python") ---//settings.linker.libs:add("/opt/local/lib/python2.5/config/libpython2.5.a") ---settings.linker.frameworks:add("Python") - -src = Collect("src/*.cpp") -objs = Compile(settings, src) -exe = Link(settings, "server", objs) - ---Target(baselib.lib) -Target(exe) diff --git a/masterserver/include/common.h b/masterserver/include/common.h deleted file mode 100644 index ff03a61b5..000000000 --- a/masterserver/include/common.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _COMMON_H -#define _COMMON_H - -typedef int int32; - -#endif diff --git a/masterserver/include/masterserver.h b/masterserver/include/masterserver.h deleted file mode 100644 index 7e042aaac..000000000 --- a/masterserver/include/masterserver.h +++ /dev/null @@ -1,48 +0,0 @@ -#ifndef _MASTERSERVER_H -#define _MASTERSERVER_H - -#include -#include "serverinfo.h" - -using namespace baselib; - -#define HEARTBEAT_SIZE 216 -#define HEARTBEAT_SIGNATURE 'TWHB' -#define HEARTBEAT_LIFETIME 10 -#define MAXSERVERS 1024 -#define SERVERINFOOUT_SIZE 212 -#define SERVERINFOHEADER_SIZE 12 -#define MASTERSERVER_VERSION 0 - -class CMasterServer -{ - CServerInfo m_Servers[MAXSERVERS]; - int m_ServerCount; - socket_udp4 m_UDPSocket; - socket_tcp4 m_TCPSocket; - int m_CurrentTime; - char m_ServerListPacket[MAXSERVERS * SERVERINFOOUT_SIZE + SERVERINFOHEADER_SIZE]; - int m_ServerListPacketSize; - bool m_ServerListPacketIsOld; - - void ListenForServerListPolls(); - void BuildServerListPacket(); - void ListenForHeartBeats(); - void ProcessHeartBeat(CServerInfo info); - CServerInfo *FindServerInfo(int32 ip, int32 port); - CServerInfo *GetUnusedSlot(); - void CleanUpServerList(); -public: - CMasterServer() - { - m_ServerCount = 0; - m_ServerListPacketIsOld = true; - } - - void Init(int port); - void Shutdown(); - - void Tick(); -}; - -#endif diff --git a/masterserver/include/network.h b/masterserver/include/network.h deleted file mode 100644 index 455fe681f..000000000 --- a/masterserver/include/network.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _NETWORK_H -#define _NETWORK_H - -#include -#include "common.h" - -char *WriteInt32(char *buffer, int32 value); -char *WriteFixedString(char *buffer, const char *string, int strlen); - -char *ReadInt32(char *buffer, int32 *value); -char *ReadFixedString(char *buffer, char *string, int strlen); - -#endif diff --git a/masterserver/include/serverinfo.h b/masterserver/include/serverinfo.h deleted file mode 100644 index c63e11ba7..000000000 --- a/masterserver/include/serverinfo.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef _SERVERINFO_H -#define _SERVERINFO_H - -#include - -#include "common.h" -#include "network.h" - -class CServerInfo -{ - int32 m_Version; - int32 m_IP; - int32 m_Port; - int32 m_Players; - int32 m_MaxPlayers; - char m_Name[128]; - char m_Map[64]; - - int m_LastRefresh; - -public: - int32 IP() const { return m_IP; } - int32 Port() const { return m_Port; } - int32 Players() const { return m_Players; } - int32 MaxPlayers() const { return m_MaxPlayers; }; - const char *Name() const { return m_Name; } - const char *Map() const { return m_Map; } - - void Refresh(int time) { m_LastRefresh = time; } - int LastRefresh() { return m_LastRefresh; } - - void SetAddress(baselib::netaddr4 *addr) - { - m_IP = addr->ip[0] << 24; - m_IP |= addr->ip[1] << 16; - m_IP |= addr->ip[2] << 8; - m_IP |= addr->ip[3]; - - m_Port = addr->port; - } - - char *Serialize(char *buffer) const - { - buffer = WriteInt32(buffer, m_Version); - buffer = WriteInt32(buffer, m_IP); - buffer = WriteInt32(buffer, m_Port); - buffer = WriteInt32(buffer, m_Players); - buffer = WriteInt32(buffer, m_MaxPlayers); - buffer = WriteFixedString(buffer, m_Name, sizeof(m_Name)); - buffer = WriteFixedString(buffer, m_Map, sizeof(m_Map)); - - return buffer; - } - - char *Deserialize(char *buffer) - { - buffer = ReadInt32(buffer, &m_Version); - buffer = ReadInt32(buffer, &m_IP); - buffer = ReadInt32(buffer, &m_Port); - buffer = ReadInt32(buffer, &m_Players); - buffer = ReadInt32(buffer, &m_MaxPlayers); - buffer = ReadFixedString(buffer, m_Name, sizeof(m_Name)); - buffer = ReadFixedString(buffer, m_Map, sizeof(m_Map)); - - return buffer; - } -}; - -#endif diff --git a/masterserver/src/main.cpp b/masterserver/src/main.cpp deleted file mode 100644 index 380830ad6..000000000 --- a/masterserver/src/main.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include -#include - -#include "masterserver.h" - -int main(int argc, char *argv[]) -{ - if (argc != 2) - { - puts("Usage: masterserver (this will bind the server to the port specified (both udp and tcp)."); - return -1; - } - - int port = atoi(argv[1]); - - CMasterServer masterServer; - masterServer.Init(port); - - while (1) - { - masterServer.Tick(); - - thread_sleep(10); - } - - masterServer.Shutdown(); - - return 0; -} diff --git a/masterserver/src/masterserver.cpp b/masterserver/src/masterserver.cpp deleted file mode 100644 index 383c696ef..000000000 --- a/masterserver/src/masterserver.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include - -#include "masterserver.h" - -void CMasterServer::Init(int port) -{ - netaddr4 addr(0, 0, 0, 0, port); - addr.port = port; - - net_init(); - m_UDPSocket.open(port); - m_TCPSocket.open(&addr); - m_TCPSocket.set_non_blocking(); - m_TCPSocket.listen(); -} - -void CMasterServer::Shutdown() -{ - m_UDPSocket.close(); -} - -void CMasterServer::Tick() -{ - m_CurrentTime = time(NULL); - - ListenForHeartBeats(); - ListenForServerListPolls(); - - CleanUpServerList(); -} - -void CMasterServer::ListenForHeartBeats() -{ - netaddr4 from; - char data[1024]; - int dataSize; - - // read udp data while there is data to read :) - while ((dataSize = m_UDPSocket.recv(&from, (char *)data, sizeof(data))) > 0) - { - // compare the received data size to the expected size - if (dataSize == HEARTBEAT_SIZE) - { - char *d = data; - int32 signature; - d = ReadInt32(d, &signature); - - // make sure the signature is correct - if (signature == HEARTBEAT_SIGNATURE) - { - CServerInfo info; - info.Deserialize(d); - - from.port = 8303; - info.SetAddress(&from); - - dbg_msg("got heartbeat", "IP: %i.%i.%i.%i:%i", (int)from.ip[0], (int)from.ip[1], (int)from.ip[2], (int)from.ip[3], from.port); - - dbg_msg("refresh", "okay. server count: %i", m_ServerCount); - - ProcessHeartBeat(info); - } - else - {} // unexpected signature - } - else - {} // unknown data received - } -} - -void CMasterServer::ProcessHeartBeat(CServerInfo info) -{ - // find the corresponding server info - CServerInfo *serverInfo = FindServerInfo(info.IP(), info.Port()); - - // if it isn't in the list already, try to get an unused slot - if (!serverInfo) - serverInfo = GetUnusedSlot(); - - // if we STILL don't have one, we're out of luck. - if (!serverInfo) - return; - - *serverInfo = info; - serverInfo->Refresh(m_CurrentTime); - - // mark the server list packet as old - m_ServerListPacketIsOld = true; -} - -CServerInfo *CMasterServer::FindServerInfo(int32 ip, int32 port) -{ - // for now, just loop over the array - for (int i = 0; i < m_ServerCount; i++) - { - CServerInfo *info = &m_Servers[i]; - if (info->IP() == ip && info->Port() == port) - return info; - } - - return 0x0; -} - -CServerInfo *CMasterServer::GetUnusedSlot() -{ - if (m_ServerCount == MAXSERVERS) - return 0x0; - else - return &m_Servers[m_ServerCount++]; -} - -void CMasterServer::CleanUpServerList() -{ - for (int i = 0; i < m_ServerCount; i++) - { - CServerInfo *serverInfo = &m_Servers[i]; - - // check if it's time to remove it from the list - if (serverInfo->LastRefresh() + HEARTBEAT_LIFETIME < m_CurrentTime) - { - if (i + 1 == m_ServerCount) - { - // if we're at the last one, just decrease m_ServerCount - --m_ServerCount; - } - else - { - // otherwise, copy the last slot here and then decrease i and m_ServerCount - *serverInfo = m_Servers[m_ServerCount - 1]; - --i; - --m_ServerCount; - } - - // mark the server list packet as old and outdated - m_ServerListPacketIsOld = true; - } - } -} - -void CMasterServer::ListenForServerListPolls() -{ - socket_tcp4 client; - - // accept clients while there are clients to be accepted - while (m_TCPSocket.accept(&client)) - { - // maybe we've prepared the packet already... it'd be silly to do it twice - if (m_ServerListPacketIsOld) - { - BuildServerListPacket(); - } - - // send the server list and then close the socket - client.send(m_ServerListPacket, m_ServerListPacketSize); - client.close(); - } -} - -void CMasterServer::BuildServerListPacket() -{ - char *d = m_ServerListPacket; - - d = WriteInt32(d, 'TWSL'); - d = WriteInt32(d, MASTERSERVER_VERSION); - - d = WriteInt32(d, m_ServerCount); - - for (int i = 0; i < m_ServerCount; i++) - { - CServerInfo *info = &m_Servers[i]; - d = info->Serialize(d); - } - - m_ServerListPacketSize = d - m_ServerListPacket; - m_ServerListPacketIsOld = false; -} diff --git a/masterserver/src/network.cpp b/masterserver/src/network.cpp deleted file mode 100644 index 7d557cdff..000000000 --- a/masterserver/src/network.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include "common.h" -#include "network.h" - -char *WriteInt32(char *buffer, int32 value) -{ - buffer[0] = value >> 24; - buffer[1] = value >> 16; - buffer[2] = value >> 8; - buffer[3] = value; - - return buffer + sizeof(int32); -} - -char *WriteFixedString(char *buffer, const char *string, int strlen) -{ - memcpy(buffer, string, strlen); - - return buffer + strlen; -} - - - -char *ReadInt32(char *buffer, int32 *value) -{ - *value = buffer[0] << 24; - *value |= buffer[1] << 16; - *value |= buffer[2] << 8; - *value |= buffer[3]; - - return buffer + sizeof(int32); -} - -char *ReadFixedString(char *buffer, char *string, int strlen) -{ - memcpy(string, buffer, strlen); - - return buffer + strlen; -} diff --git a/scripts/compiler.py b/scripts/compiler.py index 5d90e8828..5d6a814ad 100755 --- a/scripts/compiler.py +++ b/scripts/compiler.py @@ -476,7 +476,7 @@ class translator: #include #include -void patch_ptr(char **ptr, char *base) +static void patch_ptr(char **ptr, char *base) { *ptr = base+(size_t)(*ptr); } diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index 57b3bd574..816a30e7c 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -20,6 +20,8 @@ #include #include +#include + using namespace baselib; // --- input wrappers --- @@ -156,15 +158,92 @@ int client_send_msg() return 0; } +static struct +{ + server_info infos[MAX_SERVERS]; + int64 request_times[MAX_SERVERS]; + netaddr4 addresses[MAX_SERVERS]; + int num; +} servers; + +static netaddr4 master_server; + +int client_serverbrowse_getlist(server_info **serverlist) +{ + *serverlist = servers.infos; + return servers.num; +} + +void client_serverbrowse_refresh() +{ + dbg_msg("client", "requesting server list"); + NETPACKET packet; + packet.client_id = -1; + packet.address = master_server; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_GETLIST); + packet.data = SERVERBROWSE_GETLIST; + net.send(&packet); + + // reset the list + servers.num = 0; +} + +enum +{ + STATE_OFFLINE, + STATE_CONNECTING, + STATE_LOADING, + STATE_ONLINE, + STATE_BROKEN, + STATE_QUIT, +}; + +static netaddr4 server_address; + +static int state; +static int get_state() { return state; } +static void set_state(int s) +{ + dbg_msg("game", "state change. last=%d current=%d", state, s); + state = s; +} + + +void client_connect(const char *server_address_str) +{ + char buf[512]; + strncpy(buf, server_address_str, 512); + + const char *port_str = 0; + for(int k = 0; buf[k]; k++) + { + if(buf[k] == ':') + { + port_str = &(buf[k+1]); + buf[k] = 0; + break; + } + } + + int port = 8303; + if(port_str) + port = atoi(port_str); + + if(net_host_lookup(buf, port, &server_address) != 0) + dbg_msg("client", "could not find the address of %s, connecting to localhost", buf); + + net.connect(&server_address); + set_state(STATE_CONNECTING); +} + // --- client --- // TODO: remove this class class client { public: - - //socket_udp4 socket; - //connection conn; - int64 reconnect_timer; + int info_request_begin; + int info_request_end; int snapshot_part; @@ -173,24 +252,6 @@ public: // data to hold three snapshots // previous, - enum - { - STATE_OFFLINE, - STATE_CONNECTING, - STATE_LOADING, - STATE_ONLINE, - STATE_BROKEN, - STATE_QUIT, - }; - - int state; - int get_state() { return state; } - void set_state(int s) - { - dbg_msg("game", "state change. last=%d current=%d", state, s); - state = s; - } - void send_info() { recived_snapshots = 0; @@ -242,12 +303,6 @@ public: map_unload(); } - void connect(netaddr4 *server_address) - { - net.connect(server_address); - set_state(STATE_CONNECTING); - } - bool load_data() { debug_font = gfx_load_texture("data/debug_font.png"); @@ -293,13 +348,11 @@ public: } else if (get_state() != STATE_CONNECTING && get_state() != STATE_LOADING) { - netaddr4 server_address; - int status = modmenu_render(&server_address); + //netaddr4 server_address; + int status = modmenu_render(); if (status == -1) set_state(STATE_QUIT); - else if (status) - connect(&server_address); } else if (get_state() == STATE_CONNECTING || get_state() == STATE_LOADING) { @@ -332,7 +385,7 @@ public: } } - void run(netaddr4 *server_address) + void run(const char *direct_connect_server) { local_start_time = time_get(); snapshot_part = 0; @@ -356,20 +409,16 @@ public: // init menu modmenu_init(); - net.open(0); - // open socket - /* - if(!socket.open(0)) - { - dbg_msg("network/client", "failed to open socket"); - return; - }*/ + net.open(0, 0); + + // + net_host_lookup(MASTERSERVER_ADDRESS, MASTERSERVER_PORT, &master_server); // connect to the server if wanted - if(server_address) - connect(server_address); - + if(direct_connect_server) + client_connect(direct_connect_server); + //int64 inputs_per_second = 50; //int64 time_per_input = time_freq()/inputs_per_second; int64 game_starttime = time_get(); @@ -413,6 +462,9 @@ public: // pump the network pump_network(); + // update the server browser + serverbrowse_update(); + // render render(); @@ -457,146 +509,235 @@ public: send_error(msg); set_state(STATE_BROKEN); } + + void serverbrowse_request(int id) + { + dbg_msg("client", "requesting server info from %d.%d.%d.%d:%d", + servers.addresses[id].ip[0], servers.addresses[id].ip[1], servers.addresses[id].ip[2], + servers.addresses[id].ip[3], servers.addresses[id].port); + NETPACKET packet; + packet.client_id = -1; + packet.address = servers.addresses[id]; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_GETINFO); + packet.data = SERVERBROWSE_GETINFO; + net.send(&packet); + servers.request_times[id] = time_get(); + } + + void serverbrowse_update() + { + int64 timeout = time_freq(); + int64 now = time_get(); + int max_requests = 10; + + // timeout old requests + while(info_request_begin < servers.num && info_request_begin < info_request_end) + { + if(now > servers.request_times[info_request_begin]+timeout) + info_request_begin++; + else + break; + } + + // send new requests + while(info_request_end < servers.num && info_request_end-info_request_begin < max_requests) + { + serverbrowse_request(info_request_end); + info_request_end++; + } + } void process_packet(NETPACKET *packet) { - int sys; - int msg = msg_unpack_start(packet->data, packet->data_size, &sys); - if(sys) + if(packet->client_id == -1) { - // system message - if(msg == NETMSG_MAP) + // connectionlesss + if(packet->data_size >= (int)sizeof(SERVERBROWSE_LIST) && + memcmp(packet->data, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)) == 0) { - const char *map = msg_unpack_string(); - dbg_msg("client/network", "connection accepted, map=%s", map); - set_state(STATE_LOADING); - - if(map_load(map)) + // server listing + int size = packet->data_size-sizeof(SERVERBROWSE_LIST); + mem_copy(servers.addresses, (char*)packet->data+sizeof(SERVERBROWSE_LIST), size); + servers.num = size/sizeof(NETADDR4); + + info_request_begin = 0; + info_request_end = 0; + + for(int i = 0; i < servers.num; i++) { - modc_entergame(); - send_entergame(); - dbg_msg("client/network", "loading done"); - // now we will wait for two snapshots - // to finish the connection - } - else - { - error("failure to load map"); + servers.infos[i].num_players = 0; + servers.infos[i].max_players = 0; + servers.infos[i].latency = 999; + sprintf(servers.infos[i].address, "%d.%d.%d.%d:%d", + servers.addresses[i].ip[0], servers.addresses[i].ip[1], servers.addresses[i].ip[2], + servers.addresses[i].ip[3], servers.addresses[i].port); } } - else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY) + + if(packet->data_size >= (int)sizeof(SERVERBROWSE_INFO) && + memcmp(packet->data, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)) == 0) { - //dbg_msg("client/network", "got snapshot"); - int game_tick = msg_unpack_int(); - int delta_tick = game_tick-msg_unpack_int(); - int num_parts = 1; - int part = 0; - int part_size = 0; + // we got ze info + data_unpacker unpacker; + unpacker.reset((unsigned char*)packet->data+sizeof(SERVERBROWSE_INFO), packet->data_size-sizeof(SERVERBROWSE_INFO)); - if(msg == NETMSG_SNAP) + for(int i = 0; i < servers.num; i++) { - num_parts = msg_unpack_int(); - part = msg_unpack_int(); - } - - if(msg != NETMSG_SNAPEMPTY) - part_size = msg_unpack_int(); - - if(snapshot_part == part) - { - // TODO: clean this up abit - const char *d = (const char *)msg_unpack_raw(part_size); - mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); - snapshot_part++; - - if(snapshot_part == num_parts) + if(net_addr4_cmp(&servers.addresses[i], &packet->address) == 0) { - snapshot *tmp = snapshots[SNAP_PREV]; - snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; - snapshots[SNAP_CURRENT] = tmp; - current_tick = game_tick; - - // decompress snapshot - void *deltadata = snapshot_empty_delta(); - int deltasize = sizeof(int)*3; - - unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; - unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; - if(part_size) - { - int compsize = zerobit_decompress(snapshots[SNAP_INCOMMING], part_size, tmpbuffer); - int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); - deltadata = tmpbuffer2; - deltasize = intsize; - } - - // find snapshot that we should use as delta - static snapshot emptysnap; - emptysnap.data_size = 0; - emptysnap.num_items = 0; - - snapshot *deltashot = &emptysnap; - int deltashot_size; - - if(delta_tick >= 0) - { - void *delta_data; - deltashot_size = snapshots_new.get(delta_tick, &delta_data); - if(deltashot_size >= 0) - { - deltashot = (snapshot *)delta_data; - } - else - { - // TODO: handle this - dbg_msg("client", "error, couldn't find the delta snapshot"); - } - } - - int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)snapshots[SNAP_CURRENT], deltadata, deltasize); - //snapshot *shot = (snapshot *)snapshots[SNAP_CURRENT]; - - // purge old snapshots - snapshots_new.purge_until(delta_tick); - snapshots_new.purge_until(game_tick-50); // TODO: change this to server tickrate - - // add new - snapshots_new.add(game_tick, snapsize, snapshots[SNAP_CURRENT]); - - // apply snapshot, cycle pointers - recived_snapshots++; - snapshot_start_time = time_get(); - - // we got two snapshots until we see us self as connected - if(recived_snapshots == 2) - { - local_start_time = time_get(); - set_state(STATE_ONLINE); - } - - if(recived_snapshots > 2) - modc_newsnapshot(); - - snapshot_part = 0; - - // ack snapshot - msg_pack_start_system(NETMSG_SNAPACK, 0); - msg_pack_int(game_tick); - msg_pack_end(); - client_send_msg(); + strncpy(servers.infos[i].name, unpacker.get_string(), 128); + strncpy(servers.infos[i].map, unpacker.get_string(), 128); + servers.infos[i].max_players = unpacker.get_int(); + servers.infos[i].num_players = unpacker.get_int(); + servers.infos[i].latency = ((time_get() - servers.request_times[i])*1000)/time_freq(); + dbg_msg("client", "got server info"); + break; } } - else - { - dbg_msg("client", "snapshot reset!"); - snapshot_part = 0; - } } } else { - // game message - modc_message(msg); + + int sys; + int msg = msg_unpack_start(packet->data, packet->data_size, &sys); + if(sys) + { + // system message + if(msg == NETMSG_MAP) + { + const char *map = msg_unpack_string(); + dbg_msg("client/network", "connection accepted, map=%s", map); + set_state(STATE_LOADING); + + if(map_load(map)) + { + modc_entergame(); + send_entergame(); + dbg_msg("client/network", "loading done"); + // now we will wait for two snapshots + // to finish the connection + } + else + { + error("failure to load map"); + } + } + else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSMALL || msg == NETMSG_SNAPEMPTY) + { + //dbg_msg("client/network", "got snapshot"); + int game_tick = msg_unpack_int(); + int delta_tick = game_tick-msg_unpack_int(); + int num_parts = 1; + int part = 0; + int part_size = 0; + + if(msg == NETMSG_SNAP) + { + num_parts = msg_unpack_int(); + part = msg_unpack_int(); + } + + if(msg != NETMSG_SNAPEMPTY) + part_size = msg_unpack_int(); + + if(snapshot_part == part) + { + // TODO: clean this up abit + const char *d = (const char *)msg_unpack_raw(part_size); + mem_copy((char*)snapshots[SNAP_INCOMMING] + part*MAX_SNAPSHOT_PACKSIZE, d, part_size); + snapshot_part++; + + if(snapshot_part == num_parts) + { + snapshot *tmp = snapshots[SNAP_PREV]; + snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT]; + snapshots[SNAP_CURRENT] = tmp; + current_tick = game_tick; + + // decompress snapshot + void *deltadata = snapshot_empty_delta(); + int deltasize = sizeof(int)*3; + + unsigned char tmpbuffer[MAX_SNAPSHOT_SIZE]; + unsigned char tmpbuffer2[MAX_SNAPSHOT_SIZE]; + if(part_size) + { + int compsize = zerobit_decompress(snapshots[SNAP_INCOMMING], part_size, tmpbuffer); + int intsize = intpack_decompress(tmpbuffer, compsize, tmpbuffer2); + deltadata = tmpbuffer2; + deltasize = intsize; + } + + // find snapshot that we should use as delta + static snapshot emptysnap; + emptysnap.data_size = 0; + emptysnap.num_items = 0; + + snapshot *deltashot = &emptysnap; + int deltashot_size; + + if(delta_tick >= 0) + { + void *delta_data; + deltashot_size = snapshots_new.get(delta_tick, &delta_data); + if(deltashot_size >= 0) + { + deltashot = (snapshot *)delta_data; + } + else + { + // TODO: handle this + dbg_msg("client", "error, couldn't find the delta snapshot"); + } + } + + int snapsize = snapshot_unpack_delta(deltashot, (snapshot*)snapshots[SNAP_CURRENT], deltadata, deltasize); + //snapshot *shot = (snapshot *)snapshots[SNAP_CURRENT]; + + // purge old snapshots + snapshots_new.purge_until(delta_tick); + snapshots_new.purge_until(game_tick-50); // TODO: change this to server tickrate + + // add new + snapshots_new.add(game_tick, snapsize, snapshots[SNAP_CURRENT]); + + // apply snapshot, cycle pointers + recived_snapshots++; + snapshot_start_time = time_get(); + + // we got two snapshots until we see us self as connected + if(recived_snapshots == 2) + { + local_start_time = time_get(); + set_state(STATE_ONLINE); + } + + if(recived_snapshots > 2) + modc_newsnapshot(); + + snapshot_part = 0; + + // ack snapshot + msg_pack_start_system(NETMSG_SNAPACK, 0); + msg_pack_int(game_tick); + msg_pack_end(); + client_send_msg(); + } + } + else + { + dbg_msg("client", "snapshot reset!"); + snapshot_part = 0; + } + } + } + else + { + // game message + modc_message(msg); + } } } @@ -636,11 +777,8 @@ int main(int argc, char **argv) config_reset(); config_load("teewars.cfg"); + const char *direct_connect_server = 0x0; snd_set_master_volume(config.volume / 255.0f); - - netaddr4 server_address(127, 0, 0, 1, 8303); - //const char *name = "nameless jerk"; - bool connect_at_once = false; bool editor = false; // init network, need to be done first so we can do lookups @@ -653,24 +791,7 @@ int main(int argc, char **argv) { // -c SERVER:PORT i++; - const char *port_str = 0; - for(int k = 0; argv[i][k]; k++) - { - if(argv[i][k] == ':') - { - port_str = &(argv[i][k+1]); - argv[i][k] = 0; - break; - } - } - int port = 8303; - if(port_str) - port = atoi(port_str); - - if(net_host_lookup(argv[i], port, &server_address) != 0) - dbg_msg("main", "could not find the address of %s, connecting to localhost", argv[i]); - else - connect_at_once = true; + direct_connect_server = argv[i]; } else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1) { @@ -695,7 +816,7 @@ int main(int argc, char **argv) { // start the client client c; - c.run(connect_at_once ? &server_address : 0x0); + c.run(direct_connect_server); } return 0; } diff --git a/src/engine/interface.h b/src/engine/interface.h index d23067fb6..4cadb9969 100644 --- a/src/engine/interface.h +++ b/src/engine/interface.h @@ -689,7 +689,7 @@ void modmenu_shutdown(); Function: modmenu_render Called every frame to let the menu render it self. */ -int modmenu_render(void *server_address); +int modmenu_render(); @@ -752,9 +752,19 @@ float gfx_pretty_text_width(float size, const char *text); void mods_message(int msg, int client_id); void modc_message(int msg); -#define MASTER_SERVER_ADDRESS "master.teewars.com" -#define MASTER_SERVER_PORT 8300 +struct server_info +{ + int max_players; + int num_players; + int latency; // in ms + char name[128]; + char map[128]; + char address[128]; +}; +void client_connect(const char *address); +void client_serverbrowse_refresh(); +int client_serverbrowse_getlist(server_info **servers); #endif diff --git a/src/engine/network.cpp b/src/engine/network.cpp index 3abe44b0a..c6b8207fc 100644 --- a/src/engine/network.cpp +++ b/src/engine/network.cpp @@ -18,20 +18,6 @@ unsigned char crc[2]; 6 */ - -// move -static int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b) -{ - if( a->ip[0] != b->ip[0] || - a->ip[1] != b->ip[1] || - a->ip[2] != b->ip[2] || - a->ip[3] != b->ip[3] || - a->port != b->port - ) - return 1; - return 0; -} - enum { NETWORK_VERSION = 1, @@ -51,7 +37,7 @@ enum NETWORK_PACKETFLAG_CLOSE=0x04, NETWORK_PACKETFLAG_VITAL=0x08, NETWORK_PACKETFLAG_RESEND=0x10, - //NETWORK_PACKETFLAG_STATELESS=0x20, + NETWORK_PACKETFLAG_CONNLESS=0x20, }; struct NETPACKETDATA @@ -208,7 +194,7 @@ static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void p.data_size = data_size; p.data = (unsigned char *)data; p.first_send_time = time_get(); - + if(flags&NETWORK_PACKETFLAG_VITAL) { // save packet if we need to resend @@ -321,7 +307,7 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr) static void conn_update(NETCONNECTION *conn) { - if(conn->state == NETWORK_CONNSTATE_ERROR) + if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR) return; // check for timeout @@ -479,58 +465,71 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet) int r = check_packet(s->recv_buffer, bytes, &data); if(r == 0) { - // ok packet, process it - if(data.flags == NETWORK_PACKETFLAG_CONNECT) + if(data.flags&NETWORK_PACKETFLAG_CONNLESS) { - int found = 0; - - // check if we already got this client - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) - { - if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE && - net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) - { - found = 1; // silent ignore.. we got this client already - break; - } - } - - // client that wants to connect - if(!found) - { - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) - { - if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE) - { - conn_feed(&s->slots[i].conn, &data, &addr); - found = 1; - break; - } - } - } - - if(!found) - { - // TODO: send error - } + // connection less packets + packet->client_id = -1; + packet->address = addr; + packet->flags = PACKETFLAG_CONNLESS; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; } else { - // find matching slot - for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + // ok packet, process it + if(data.flags == NETWORK_PACKETFLAG_CONNECT) { - if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) + int found = 0; + + // check if we already got this client + for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) { - if(conn_feed(&s->slots[i].conn, &data, &addr)) + if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE && + net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) { - if(data.data_size) + found = 1; // silent ignore.. we got this client already + break; + } + } + + // client that wants to connect + if(!found) + { + for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + { + if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE) { - packet->client_id = i; - packet->address = addr; - packet->flags = 0; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; + conn_feed(&s->slots[i].conn, &data, &addr); + found = 1; + break; + } + } + } + + if(!found) + { + // TODO: send error + } + } + else + { + // find matching slot + for(int i = 0; i < NETWORK_MAX_CLIENTS; i++) + { + if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0) + { + if(conn_feed(&s->slots[i].conn, &data, &addr)) + { + if(data.data_size) + { + packet->client_id = i; + packet->address = addr; + packet->flags = 0; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } } } } @@ -540,6 +539,7 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet) else { // errornous packet, drop it + dbg_msg("server", "crazy packet"); } // read header @@ -551,10 +551,27 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet) int net_server_send(NETSERVER *s, NETPACKET *packet) { - // TODO: insert stuff for stateless stuff - dbg_assert(packet->client_id >= 0, "errornous client id"); - dbg_assert(packet->client_id < NETWORK_MAX_CLIENTS, "errornous client id"); - conn_send(&s->slots[packet->client_id].conn, 0, packet->data_size, packet->data); + if(packet->flags&PACKETFLAG_CONNLESS) + { + // send connectionless packet + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CONNLESS; + p.seq = 0; + p.ack = 0; + p.crc = 0; + p.data_size = packet->data_size; + p.data = (unsigned char *)packet->data; + send_packet(s->socket, &packet->address, &p); + } + else + { + dbg_assert(packet->client_id >= 0, "errornous client id"); + dbg_assert(packet->client_id < NETWORK_MAX_CLIENTS, "errornous client id"); + conn_send(&s->slots[packet->client_id].conn, 0, packet->data_size, packet->data); + } return 0; } @@ -574,11 +591,11 @@ void net_server_stats(NETSERVER *s, NETSTATS *stats) } // -NETCLIENT *net_client_open(int flags) +NETCLIENT *net_client_open(int port, int flags) { NETCLIENT *client = (NETCLIENT *)mem_alloc(sizeof(NETCLIENT), 1); mem_zero(client, sizeof(NETCLIENT)); - client->socket = net_udp4_create(0); + client->socket = net_udp4_create(port); conn_init(&client->conn, client->socket); return client; } @@ -626,23 +643,37 @@ int net_client_recv(NETCLIENT *c, NETPACKET *packet) NETPACKETDATA data; int r = check_packet(c->recv_buffer, bytes, &data); - if(r == 0 && conn_feed(&c->conn, &data, &addr)) - { - // fill in packet - packet->client_id = 0; - packet->address = addr; - packet->flags = 0; - packet->data_size = data.data_size; - packet->data = data.data; - return 1; - } - else - { - // errornous packet, drop it - } - // read header - // do checksum + if(r == 0) + { + if(data.flags&NETWORK_PACKETFLAG_CONNLESS) + { + // connection less packets + packet->client_id = -1; + packet->address = addr; + packet->flags = PACKETFLAG_CONNLESS; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + if(conn_feed(&c->conn, &data, &addr)) + { + // fill in packet + packet->client_id = 0; + packet->address = addr; + packet->flags = 0; + packet->data_size = data.data_size; + packet->data = data.data; + return 1; + } + else + { + // errornous packet, drop it + } + } + } } return 0; @@ -650,9 +681,27 @@ int net_client_recv(NETCLIENT *c, NETPACKET *packet) int net_client_send(NETCLIENT *c, NETPACKET *packet) { - // TODO: insert stuff for stateless stuff - dbg_assert(packet->client_id == 0, "errornous client id"); - conn_send(&c->conn, 0, packet->data_size, packet->data); + if(packet->flags&PACKETFLAG_CONNLESS) + { + // send connectionless packet + NETPACKETDATA p; + p.ID[0] = 'T'; + p.ID[1] = 'W'; + p.version = NETWORK_VERSION; + p.flags = NETWORK_PACKETFLAG_CONNLESS; + p.seq = 0; + p.ack = 0; + p.crc = 0; + p.data_size = packet->data_size; + p.data = (unsigned char *)packet->data; + send_packet(c->socket, &packet->address, &p); + } + else + { + // TODO: insert stuff for stateless stuff + dbg_assert(packet->client_id == 0, "errornous client id"); + conn_send(&c->conn, 0, packet->data_size, packet->data); + } return 0; } diff --git a/src/engine/network.h b/src/engine/network.h index de0358886..e251f13e3 100644 --- a/src/engine/network.h +++ b/src/engine/network.h @@ -28,6 +28,7 @@ enum { NETFLAG_ALLOWSTATELESS=1, PACKETFLAG_VITAL=1, + PACKETFLAG_CONNLESS=2, NETSTATE_OFFLINE=0, NETSTATE_CONNECTING, @@ -46,7 +47,7 @@ int net_server_delclient(NETSERVER *s); // -1 when no more, else, client id void net_server_stats(NETSERVER *s, NETSTATS *stats); // client side -NETCLIENT *net_client_open(int flags); +NETCLIENT *net_client_open(int port, int flags); int net_client_disconnect(NETCLIENT *c, const char *reason); int net_client_connect(NETCLIENT *c, NETADDR4 *addr); int net_client_recv(NETCLIENT *c, NETPACKET *packet); @@ -88,7 +89,7 @@ public: net_client() : ptr(0) {} ~net_client() { close(); } - int open(int flags) { ptr = net_client_open(flags); return ptr != 0; } + int open(int port, int flags) { ptr = net_client_open(port, flags); return ptr != 0; } int close() { int r = net_client_close(ptr); ptr = 0; return r; } int connect(NETADDR4 *addr) { return net_client_connect(ptr, addr); } diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index 6fe84ade6..87380cfa1 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -15,6 +15,7 @@ #include #include +#include namespace baselib {} using namespace baselib; @@ -162,11 +163,14 @@ public: //for(int i = 0; i < MAX_CLIENTS; i++) //dbg_msg("network/server", "\t%d: %d", i, clients[i].state); - if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &master_server) != 0) + if (net_host_lookup(MASTERSERVER_ADDRESS, MASTERSERVER_PORT, &master_server) != 0) { // TODO: fix me //master_server = netaddr4(0, 0, 0, 0, 0); } + + dbg_msg("server", "masterserver = %d.%d.%d.%d:%d", + master_server.ip[0], master_server.ip[1], master_server.ip[2], master_server.ip[3], master_server.port); mods_init(); @@ -178,13 +182,15 @@ public: lastheartbeat = 0; int64 reporttime = time_get(); - int64 reportinterval = time_freq()*3; + int reportinterval = 3; + //int64 reportinterval = time_freq()*3; int64 simulationtime = 0; int64 snaptime = 0; int64 networktime = 0; + int64 totaltime = 0; - while(1) + while(true) { int64 t = time_get(); if(t-lasttick > time_per_tick) @@ -204,23 +210,11 @@ public: lasttick += time_per_tick; } - if(send_heartbeats) + //if(send_heartbeats) { if (t > lastheartbeat+time_per_heartbeat) { - if (master_server.port != 0) - { - int players = 0; - - for (int i = 0; i < MAX_CLIENTS; i++) - if (!clients[i].is_empty()) - players++; - - // TODO: fix me - netaddr4 me(127, 0, 0, 0, 8303); - //send_heartbeat(0, &me, players, MAX_CLIENTS, server_name, mapname); - } - + send_heartbeat(); lastheartbeat = t+time_per_heartbeat; } } @@ -233,37 +227,21 @@ public: if(reporttime < time_get()) { - int64 totaltime = simulationtime+snaptime+networktime; dbg_msg("server/report", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%", - simulationtime/(float)reportinterval*1000, - snaptime/(float)reportinterval*1000, - networktime/(float)reportinterval*1000, - totaltime/(float)reportinterval*1000, - (simulationtime+snaptime+networktime)/(float)reportinterval*100.0f); - - unsigned sent_total=0, recv_total=0; - /* - for (int i = 0; i < MAX_CLIENTS; i++) - if (!clients[i].is_empty()) - { - unsigned s,r; - clients[i].conn.counter_get(&s,&r); - clients[i].conn.counter_reset(); - sent_total += s; - recv_total += r; - } - */ - - dbg_msg("server/report", "biggestsnap=%d send=%d recv=%d", - biggest_snapshot, sent_total/3, recv_total/3); + (simulationtime/reportinterval)/(double)time_freq()*1000, + (snaptime/reportinterval)/(double)time_freq()*1000, + (networktime/reportinterval)/(double)time_freq()*1000, + (totaltime/reportinterval)/(double)time_freq()*1000, + (totaltime)/reportinterval/(double)time_freq()*100.0f); simulationtime = 0; snaptime = 0; networktime = 0; + totaltime = 0; - reporttime += reportinterval; + reporttime += time_freq()*reportinterval; } - + totaltime += time_get()-t; thread_sleep(1); } @@ -382,6 +360,18 @@ public: msg_pack_end(); server_send_msg(cid); } + + void send_heartbeat() + { + dbg_msg("server", "sending heartbeat"); + NETPACKET packet; + packet.client_id = -1; + packet.address = master_server; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = sizeof(SERVERBROWSE_HEARTBEAT); + packet.data = SERVERBROWSE_HEARTBEAT; + net.send(&packet); + } void drop(int cid, const char *reason) { @@ -450,6 +440,27 @@ public: drop(clientId, "client timedout"); } + void send_serverinfo(NETADDR4 *addr) + { + dbg_msg("server", "sending heartbeat"); + NETPACKET packet; + + data_packer packer; + packer.reset(); + packer.add_raw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO)); + packer.add_string(server_name, 128); + packer.add_string(map_name, 128); + packer.add_int(8); // max_players + packer.add_int(0); // num_players + + packet.client_id = -1; + packet.address = *addr; + packet.flags = PACKETFLAG_CONNLESS; + packet.data_size = packer.size(); + packet.data = packer.data(); + net.send(&packet); + } + void pump_network() { net.update(); @@ -458,10 +469,16 @@ public: NETPACKET packet; while(net.recv(&packet)) { - if(packet.client_id == -1) { // stateless + if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) && + memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0) + { + dbg_msg("server", "info requested"); + send_serverinfo(&packet.address); + + } } else process_client_packet(&packet); diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp index e3a0ab767..359bb880f 100644 --- a/src/game/client/game_client.cpp +++ b/src/game/client/game_client.cpp @@ -201,7 +201,7 @@ public: { i->pos = pos; i->life = 0.75f; - i->dir = dir; + i->dir = dir*-1; i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; } } @@ -382,7 +382,7 @@ void modc_init() { // load the data container data = load_data_container("data/client.dat"); - + // load sounds for(int s = 0; s < data->num_sounds; s++) for(int i = 0; i < data->sounds[s].num_sounds; i++) @@ -505,15 +505,17 @@ static void render_projectile(obj_projectile *prev, obj_projectile *current) gfx_quads_begin(); select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj); - vec2 vel(current->vx, current->vy); + vec2 vel = mix(vec2(prev->vx, prev->vy), vec2(current->vx, current->vy), client_intratick()); + vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); - // TODO: interpolare angle aswell if(length(vel) > 0.00001f) gfx_quads_setrotation(get_angle(vel)); else gfx_quads_setrotation(0); - vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick()); + // TODO: do this, but nice + //temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f); + gfx_quads_draw(pos.x, pos.y,32,32); gfx_quads_setrotation(0); gfx_quads_end(); @@ -898,7 +900,8 @@ void modc_render() if(mouse_pos.x < 0) a = a+pi; - input.angle = (int)(a*256.0f); + input.target_x = (int)mouse_pos.x; //(int)(a*256.0f); + input.target_y = (int)mouse_pos.y; //(int)(a*256.0f); input.activeweapon = -1; if(!chat_active) @@ -912,10 +915,8 @@ void modc_render() input.blink = inp_key_pressed('S'); // Weapon switching - input.activeweapon = inp_key_pressed('1') ? 0 : input.activeweapon; - input.activeweapon = inp_key_pressed('2') ? 1 : input.activeweapon; - input.activeweapon = inp_key_pressed('3') ? 2 : input.activeweapon; - input.activeweapon = inp_key_pressed('4') ? 3 : input.activeweapon; + for(int i = 0; i < 8; i++) + input.activeweapon = inp_key_pressed('1'+i) ? i : input.activeweapon; } snap_input(&input, sizeof(input)); diff --git a/src/game/client/menu.cpp b/src/game/client/menu.cpp index 0d10116ff..96d75fd6e 100644 --- a/src/game/client/menu.cpp +++ b/src/game/client/menu.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,7 @@ #include #include "data.h" +#include extern data_container *data; @@ -234,6 +236,7 @@ void draw_teewars_button(void *id, const char *text, int checked, float x, float ui_do_label(x + w/2 - text_width/2, y, text, 46); } +/* struct server_info { int version; @@ -242,11 +245,10 @@ struct server_info netaddr4 address; char name[129]; char map[65]; -}; +};*/ struct server_list { - server_info infos[10]; int active_count, info_count; int scroll_index; int selected_index; @@ -555,7 +557,7 @@ int do_scroll_bar_vert(void *id, float x, float y, float height, int steps, int return r; } -static int do_server_list(server_list *list, float x, float y, int visible_items) +static int do_server_list(float x, float y, int *scroll_index, int *selected_index, int visible_items) { const float spacing = 3.f; const float item_height = 28; @@ -563,22 +565,27 @@ static int do_server_list(server_list *list, float x, float y, int visible_items const float real_width = item_width + 20; const float real_height = item_height * visible_items + spacing * (visible_items - 1); + server_info *servers; + int num_servers = client_serverbrowse_getlist(&servers); + int r = -1; for (int i = 0; i < visible_items; i++) { - int item_index = i + list->scroll_index; - if (item_index >= list->active_count); + int item_index = i + *scroll_index; + if (item_index >= num_servers) + ; //ui_do_image(empty_item_texture, x, y + i * item_height + i * spacing, item_width, item_height); else { - server_info *item = &list->infos[item_index]; + server_info *item = &servers[item_index]; bool clicked = false; - clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, draw_menu_button, (list->selected_index == item_index) ? (void *)1 : 0); + clicked = ui_do_button(item, item->name, 0, x, y + i * item_height + i * spacing, item_width, item_height, + draw_menu_button, (*selected_index == item_index) ? (void *)1 : 0); char temp[64]; // plenty of extra room so we don't get sad :o - sprintf(temp, "%i/%i", item->players, item->max_players); + sprintf(temp, "%i/%i %3d", item->num_players, item->max_players, item->latency); ui_do_label(x + 600, y + i * item_height + i * spacing, temp, item_height); ui_do_label(x + 360, y + i * item_height + i * spacing, item->map, item_height); @@ -586,136 +593,17 @@ static int do_server_list(server_list *list, float x, float y, int visible_items if (clicked) { r = item_index; - list->selected_index = item_index; + *selected_index = item_index; } } } - list->scroll_index = do_scroll_bar_vert(&list->scroll_index, x + real_width - 16, y, real_height, list->active_count - visible_items, list->scroll_index); + *scroll_index = do_scroll_bar_vert(scroll_index, x + real_width - 16, y, real_height, + min(num_servers - visible_items, 0), *scroll_index); return r; } -static char *read_int(char *buffer, int *value) -{ - *value = buffer[0] << 24; - *value |= buffer[1] << 16; - *value |= buffer[2] << 8; - *value |= buffer[3]; - - return buffer + 4; -} - -static char *read_netaddr(char *buffer, netaddr4 *addr) -{ - addr->ip[0] = *buffer++; - addr->ip[1] = *buffer++; - addr->ip[2] = *buffer++; - addr->ip[3] = *buffer++; - - int port; - buffer = read_int(buffer, &port); - - addr->port = port; - - return buffer; -} - -static void refresh_list(server_list *list) -{ - netaddr4 addr; - netaddr4 me(0, 0, 0, 0, 0); - - list->selected_index = -1; - - if (net_host_lookup(MASTER_SERVER_ADDRESS, MASTER_SERVER_PORT, &addr) == 0) - { - socket_tcp4 sock; - sock.open(&me); - - //sock.set_non_blocking(); - - // try and connect with a timeout of 1 second - if (sock.connect_non_blocking(&addr)) - { - char data[256]; - int total_received = 0; - int received; - - int master_server_version = -1; - int server_count = -1; - - // read header - while (total_received < 12 && (received = sock.recv(data + total_received, 12 - total_received)) > 0) - total_received += received; - - // see if we have the header - if (total_received == 12) - { - int signature; - read_int(data, &signature); - - // check signature - if(signature == 'TWSL') - { - read_int(data + 4, &master_server_version); - read_int(data + 8, &server_count); - - // TODO: handle master server version O.o - - const int server_info_size = 212; - - list->active_count = 0; - - for (int i = 0; i < server_count; i++) - { - total_received = 0; - - // read data for a server - while (sock.is_connected() && total_received < server_info_size && (received = sock.recv(data + total_received, server_info_size - total_received)) > 0) - total_received += received; - - // check if we got enough data - if (total_received == server_info_size) - { - char *d = data; - - server_info *info = &list->infos[i]; - - d = read_int(d, &info->version); - d = read_netaddr(d, &info->address); - - //dbg_msg("menu/got_serverinfo", "IP: %i.%i.%i.%i:%i", (int)info->address.ip[0], (int)info->address.ip[1], (int)info->address.ip[2], (int)info->address.ip[3], info->address.port); - - d = read_int(d, &info->players); - d = read_int(d, &info->max_players); - memcpy(info->name, d, 128); - d += 128; - memcpy(info->map, d, 64); - - // let's be safe. - info->name[128] = 0; - info->map[64] = 0; - - ++list->active_count; - } - else - break; - } - - if (list->scroll_index >= list->active_count) - list->scroll_index = list->active_count - 1; - - if (list->scroll_index < 0) - list->scroll_index = 0; - } - } - - sock.close(); - } - } -} - enum { SCREEN_MAIN, @@ -737,41 +625,45 @@ const float row2_y = row1_y + 40; const float row3_y = row2_y + 40; const float row4_y = row3_y + 40; -static int main_render(netaddr4 *server_address) +static int main_render() { - static server_list list; static bool inited = false; if (!inited) { + /* list.info_count = 256; list.scroll_index = 0; list.selected_index = -1; + */ inited = true; - refresh_list(&list); + client_serverbrowse_refresh(); } - do_server_list(&list, 20, 160, 8); + static int scoll_index = 0, selected_index = -1; + do_server_list(20, 160, &scoll_index, &selected_index, 8); static int refresh_button, join_button, quit_button; if (ui_do_button(&refresh_button, "Refresh", 0, 440, 420, 170, 48, draw_teewars_button)) - { - refresh_list(&list); - } + client_serverbrowse_refresh(); - if (list.selected_index == -1) + if (selected_index == -1) { ui_do_button(&join_button, "Join", 0, 620, 420, 128, 48, draw_teewars_button, (void *)1); } else if (ui_do_button(&join_button, "Join", 0, 620, 420, 128, 48, draw_teewars_button)) { - *server_address = list.infos[list.selected_index].address; + // *server_address = list.infos[list.selected_index].address; + + server_info *servers; + int num_servers = client_serverbrowse_getlist(&servers); - dbg_msg("menu/join_button", "IP: %i.%i.%i.%i:%i", (int)server_address->ip[0], (int)server_address->ip[1], (int)server_address->ip[2], (int)server_address->ip[3], server_address->port); + client_connect(servers[selected_index].address); + //dbg_msg("menu/join_button", "IP: %i.%i.%i.%i:%i", (int)server_address->ip[0], (int)server_address->ip[1], (int)server_address->ip[2], (int)server_address->ip[3], server_address->port); return 1; } @@ -1123,7 +1015,7 @@ static int kerning_render() return 0; } -static int menu_render(netaddr4 *server_address) +static int menu_render() { // background color gfx_clear(0.65f,0.78f,0.9f); @@ -1147,7 +1039,7 @@ static int menu_render(netaddr4 *server_address) switch (screen) { - case SCREEN_MAIN: return main_render(server_address); + case SCREEN_MAIN: return main_render(); case SCREEN_SETTINGS_GENERAL: case SCREEN_SETTINGS_CONTROLS: case SCREEN_SETTINGS_VIDEO: @@ -1176,7 +1068,7 @@ void modmenu_shutdown() { } -int modmenu_render(void *ptr) +int modmenu_render() { static int mouse_x = 0; static int mouse_y = 0; @@ -1187,8 +1079,6 @@ int modmenu_render(void *ptr) music_menu_id = snd_play(music_menu, SND_LOOP); } - netaddr4 *server_address = (netaddr4 *)ptr; - // handle mouse movement float mx, my; { @@ -1214,7 +1104,7 @@ int modmenu_render(void *ptr) } //int r = menu_render(server_address, str, max_len); - int r = menu_render(server_address); + int r = menu_render(); // render butt ugly mouse cursor // TODO: render nice cursor diff --git a/src/game/game.h b/src/game/game.h index 917abab1a..5f69ef9ed 100644 --- a/src/game/game.h +++ b/src/game/game.h @@ -58,7 +58,10 @@ struct player_input { int left; int right; - int angle; + + int target_x; + int target_y; + int jump; int fire; int hook; diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp index cf8cea906..12685fe87 100644 --- a/src/game/server/game_server.cpp +++ b/src/game/server/game_server.cpp @@ -464,7 +464,8 @@ void projectile::tick() lifespan--; // check player intersection as well - entity *targetplayer = (entity*)intersect_player(oldpos, pos, oldpos, powner); + vec2 new_pos; + entity *targetplayer = (entity*)intersect_player(oldpos, pos, new_pos, powner); if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y)) { if (lifespan >= 0) @@ -489,6 +490,74 @@ void projectile::snap(int snapping_client) proj->type = type; } + +////////////////////////////////////////////////// +// projectile_backpackrocket +////////////////////////////////////////////////// +projectile_backpackrocket::projectile_backpackrocket(baselib::vec2 pos, baselib::vec2 target, int owner, entity* powner) +: projectile(WEAPON_PROJECTILETYPE_ROCKET, owner, pos, vec2(0,0), 100, powner, 0, 0, 0, -1, WEAPON_ROCKET_BACKPACK) +{ + stage = 0; + start_tick = server_tick(); + vel = vec2(0,-10.0f); + this->target = target; + start = pos; + midpoint = pos; + midpoint.y = target.y; + direction = normalize(target-midpoint); + deply_ticks = (int)( distance(start, midpoint)/(float)server_tickspeed() * 5.0f ); + dbg_msg("rocket_bp", "%f %d", distance(start, midpoint), deply_ticks); +} + +void projectile_backpackrocket::tick() +{ + lifespan--; + if(!lifespan) + world.destroy_entity(this); + + vec2 oldpos = pos; + + if(stage == 0) + { + float time = (server_tick()-start_tick)/(float)(deply_ticks); + if(midpoint.y > start.y) + pos.y = mix(start.y, midpoint.y, 1-sinf((1-time)*pi/2)); + else + pos.y = mix(start.y, midpoint.y, sinf(time*pi/2)); + + float a = (server_tick()-start_tick)/(float)server_tickspeed()*pi*7.5f; + vel.x = sinf(a)*30.0f; + vel.y = cosf(a)*30.0f; + + if(server_tick() > start_tick+deply_ticks) + { + pos = midpoint; + direction = normalize(target-pos); + vel = vec2(0,0); + stage = 1; + } + } + else if(stage == 1) + { + vel += direction*1.5f; + vel.x = clamp(vel.x, -20.0f, 20.0f); + pos += vel; + } + + // check player intersection as well + vec2 new_pos; + entity *targetplayer = (entity*)intersect_player(oldpos, pos, new_pos, powner); + if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y)) + { + if (lifespan >= 0) + create_sound(pos, sound_impact); + + create_explosion(oldpos, owner, weapon, false); + + world.destroy_entity(this); + } +} + ////////////////////////////////////////////////// // player ////////////////////////////////////////////////// @@ -556,6 +625,11 @@ void player::respawn() weapons[WEAPON_HAMMER].ammo = -1; weapons[WEAPON_GUN].got = true; weapons[WEAPON_GUN].ammo = 10; + + // TEMP DEBUG + weapons[WEAPON_ROCKET_BACKPACK].got = true; + weapons[WEAPON_ROCKET_BACKPACK].ammo = 10; + active_weapon = WEAPON_GUN; reload_timer = 0; @@ -620,7 +694,7 @@ void player::handle_weapons() break; case WEAPON_GUN: - new projectile(WEAPON_PROJECTILETYPE_GUN, + new projectile(projectile::WEAPON_PROJECTILETYPE_GUN, client_id, pos+vec2(0,0), direction*30.0f, @@ -630,7 +704,7 @@ void player::handle_weapons() create_sound(pos, SOUND_GUN_FIRE); break; case WEAPON_ROCKET: - new projectile(WEAPON_PROJECTILETYPE_ROCKET, + new projectile(projectile::WEAPON_PROJECTILETYPE_ROCKET, client_id, pos+vec2(0,0), direction*15.0f, @@ -644,7 +718,7 @@ void player::handle_weapons() { float a = get_angle(direction); a += i*0.075f; - new projectile(WEAPON_PROJECTILETYPE_SHOTGUN, + new projectile(projectile::WEAPON_PROJECTILETYPE_SHOTGUN, client_id, pos+vec2(0,0), vec2(cosf(a), sinf(a))*25.0f, @@ -655,6 +729,13 @@ void player::handle_weapons() } create_sound(pos, SOUND_SHOTGUN_FIRE); break; + case WEAPON_ROCKET_BACKPACK: + new projectile_backpackrocket( + pos+vec2(0,0), + pos+vec2(input.target_x,input.target_y), + client_id, + this); + break; } weapons[active_weapon].ammo--; @@ -662,10 +743,11 @@ void player::handle_weapons() else { // click!!! click + // TODO: make sound here } attack_tick = server_tick(); - reload_timer = 10; // make this variable depending on weapon + reload_timer = 10; // TODO: make this variable depending on weapon } } } @@ -685,7 +767,7 @@ void player::tick() // fetch some info bool grounded = is_grounded(); - direction = get_direction(input.angle); + direction = normalize(vec2(input.target_x, input.target_y)); float max_speed = grounded ? ground_control_speed : air_control_speed; float accel = grounded ? ground_control_accel : air_control_accel; @@ -950,8 +1032,13 @@ void player::snap(int snaping_client) player->hook_active = hook_state>0?1:0; player->hook_x = (int)hook_pos.x; player->hook_y = (int)hook_pos.y; + + float a = atan((float)input.target_y/(float)input.target_x); + if(input.target_x < 0) + a = a+pi; - player->angle = input.angle; + player->angle = (int)(a*256.0f); + player->score = score; } @@ -1079,15 +1166,17 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage) // deal damage entity *ents[64]; const float radius = 128.0f; + const float innerradius = 42.0f; int num = world.find_entities(p, radius, ents, 64); for(int i = 0; i < num; i++) { vec2 diff = ents[i]->pos - p; vec2 forcedir(0,1); - if (length(diff)) - forcedir = normalize(diff); float l = length(diff); - float dmg = 5 * (1 - (l/radius)); + if(l) + forcedir = normalize(diff); + l = 1-clamp((l-innerradius)/(radius-innerradius), 0.0f, 1.0f); + float dmg = 6 * l; if((int)dmg) ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner, weapon); } diff --git a/src/game/server/game_server.h b/src/game/server/game_server.h index 027a140f3..ec3d597a5 100644 --- a/src/game/server/game_server.h +++ b/src/game/server/game_server.h @@ -146,6 +146,10 @@ public: enum { PROJECTILE_FLAGS_EXPLODE = 1 << 0, + + WEAPON_PROJECTILETYPE_GUN = 0, + WEAPON_PROJECTILETYPE_ROCKET = 1, + WEAPON_PROJECTILETYPE_SHOTGUN = 2, }; baselib::vec2 vel; @@ -166,19 +170,26 @@ public: virtual void snap(int snapping_client); }; +class projectile_backpackrocket : public projectile +{ + int stage; + int start_tick; + int deply_ticks; + baselib::vec2 target; + baselib::vec2 start; + baselib::vec2 midpoint; + baselib::vec2 direction; +public: + projectile_backpackrocket(baselib::vec2 pos, baselib::vec2 target, int owner, entity* powner); + virtual void tick(); +}; + // player entity class player : public entity { public: static const int phys_size = 28; - enum // what are these? - { - WEAPON_PROJECTILETYPE_GUN = 0, - WEAPON_PROJECTILETYPE_ROCKET = 1, - WEAPON_PROJECTILETYPE_SHOTGUN = 2, - }; - // weapon info struct weaponstat { diff --git a/src/mastersrv/mastersrv.cpp b/src/mastersrv/mastersrv.cpp new file mode 100644 index 000000000..03c06b97e --- /dev/null +++ b/src/mastersrv/mastersrv.cpp @@ -0,0 +1,118 @@ +#include +#include +#include + +#include "mastersrv.h" + +enum { + MTU = 1400, + EXPIRE_TIME = 90 +}; + +static struct packet_data +{ + unsigned char header[sizeof(SERVERBROWSE_LIST)]; + NETADDR4 servers[MAX_SERVERS]; +} data; +static int64 server_expire[MAX_SERVERS]; +static int num_servers = 0; + +static net_client net; + +void add_server(NETADDR4 *info) +{ + // see if server already exists in list + int i; + for(i = 0; i < num_servers; i++) + { + if(net_addr4_cmp(&data.servers[i], info) == 0) + { + dbg_msg("mastersrv", "updated: %d.%d.%d.%d:%d", + info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port); + server_expire[i] = time_get()+time_freq()*EXPIRE_TIME; + return; + } + } + + // add server + if(num_servers == MAX_SERVERS) + { + dbg_msg("mastersrv", "error: mastersrv is full"); + return; + } + + dbg_msg("mastersrv", "added: %d.%d.%d.%d:%d", + info->ip[0], info->ip[1], info->ip[2], info->ip[3], info->port); + data.servers[num_servers] = *info; + server_expire[num_servers] = time_get()+time_freq()*EXPIRE_TIME; + num_servers++; +} + +void purge_servers() +{ + int64 now = time_get(); + int i = 0; + while(i < num_servers) + { + if(server_expire[i] < now) + { + // remove server + dbg_msg("mastersrv", "expired: %d.%d.%d.%d:%d", + data.servers[i].ip[0], data.servers[i].ip[1], + data.servers[i].ip[2], data.servers[i].ip[3], data.servers[i].port); + data.servers[i] = data.servers[num_servers-1]; + server_expire[i] = server_expire[num_servers-1]; + num_servers--; + } + else + i++; + } +} + +int main(int argc, char **argv) +{ + net.open(MASTERSERVER_PORT, 0); + // TODO: check socket for errors + + mem_copy(data.header, SERVERBROWSE_LIST, sizeof(SERVERBROWSE_LIST)); + dbg_msg("mastersrv", "started"); + + while(1) + { + net.update(); + + // process packets + NETPACKET packet; + while(net.recv(&packet)) + { + if(packet.data_size == sizeof(SERVERBROWSE_HEARTBEAT) && + memcmp(packet.data, SERVERBROWSE_HEARTBEAT, sizeof(SERVERBROWSE_HEARTBEAT)) == 0) + { + // add it + add_server(&packet.address); + } + else if(packet.data_size == sizeof(SERVERBROWSE_GETLIST) && + memcmp(packet.data, SERVERBROWSE_GETLIST, sizeof(SERVERBROWSE_GETLIST)) == 0) + { + // someone requested the list + dbg_msg("mastersrv", "requested, responding with %d servers", num_servers); + NETPACKET p; + p.client_id = -1; + p.address = packet.address; + p.flags = PACKETFLAG_CONNLESS; + p.data_size = num_servers*sizeof(NETADDR4)+sizeof(SERVERBROWSE_LIST); + p.data = &data; + net.send(&p); + } + + } + + // TODO: shouldn't be done every fuckin frame + purge_servers(); + + // be nice to the CPU + thread_sleep(1); + } + + return 0; +} diff --git a/src/mastersrv/mastersrv.h b/src/mastersrv/mastersrv.h new file mode 100644 index 000000000..fd59808fd --- /dev/null +++ b/src/mastersrv/mastersrv.h @@ -0,0 +1,19 @@ +static const char *MASTERSERVER_ADDRESS = "localhost"; +static const int MASTERSERVER_PORT = 8383; + +static const int MAX_SERVERS = 200; + +/*enum { + SERVERBROWSE_NULL=0, + SERVERBROWSE_HEARTBEAT, + SERVERBROWSE_GETLIST, + SERVERBROWSE_LIST, + SERVERBROWSE_GETINFO, + SERVERBROWSE_INFO, +};*/ + +static const unsigned char SERVERBROWSE_HEARTBEAT[] = {255, 255, 255, 255, 'b', 'e', 'a', 't'}; +static const unsigned char SERVERBROWSE_GETLIST[] = {255, 255, 255, 255, 'r', 'e', 'q', 't'}; +static const unsigned char SERVERBROWSE_LIST[] = {255, 255, 255, 255, 'l', 'i', 's', 't'}; +static const unsigned char SERVERBROWSE_GETINFO[] = {255, 255, 255, 255, 'g', 'i', 'e', 'f'}; +static const unsigned char SERVERBROWSE_INFO[] = {255, 255, 255, 255, 'i', 'n', 'f', 'o'};