merged over all stuff from 0.2 to trunk

This commit is contained in:
Magnus Auvinen 2007-08-14 18:37:16 +00:00
parent 8809084d25
commit 2cde04ddce
28 changed files with 837 additions and 360 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -433,7 +433,7 @@ weapons {
visual_size 96
offsetx 4.0
offsety -20.0
meleedamage 3
meleedamage 4
meleereach 40
ammoregentime 0
duration -1

View file

@ -29,7 +29,6 @@ static int info_request_end;
static int snapshot_part;
static int64 local_start_time;
static int64 game_start_time;
static int current_tick;
static float latency = 0;
static int extra_polating = 0;
static int debug_font;
@ -37,8 +36,12 @@ static float frametime = 0.0001f;
static net_client net;
static netaddr4 master_server;
static netaddr4 server_address;
static const char *server_spam_address=0;
static int window_must_refocus = 0;
static int snaploss = 0;
static int current_tick = 0;
static float intratick = 0;
// --- input wrappers ---
static int keyboard_state[2][input::last];
@ -151,14 +154,9 @@ static void client_snapshot_purge_until(int tick)
static snapshot_info *snapshots[NUM_SNAPSHOT_TYPES];
static int recived_snapshots;
static int64 snapshot_start_time;
static char snapshot_incomming_data[MAX_SNAPSHOT_SIZE];
// ---
float client_localtime()
{
return (time_get()-local_start_time)/(float)(time_freq());
}
const void *snap_get_item(int snapid, int index, snap_item *item)
{
@ -199,7 +197,7 @@ static void snap_init()
// ------ time functions ------
float client_intratick()
{
return (time_get() - snapshot_start_time)/(float)(time_freq()/SERVER_TICK_SPEED);
return intratick;
}
int client_tick()
@ -217,6 +215,10 @@ float client_frametime()
return frametime;
}
float client_localtime()
{
return (time_get()-local_start_time)/(float)(time_freq());
}
int menu_loop(); // TODO: what is this?
@ -312,9 +314,11 @@ void client_serverbrowse_refresh(int lan)
if(serverlist_lan)
{
if(config.debug)
dbg_msg("client", "broadcasting for servers");
NETPACKET packet;
packet.client_id = -1;
mem_zero(&packet, sizeof(packet));
packet.address.ip[0] = 0;
packet.address.ip[1] = 0;
packet.address.ip[2] = 0;
@ -330,8 +334,10 @@ void client_serverbrowse_refresh(int lan)
}
else
{
if(config.debug)
dbg_msg("client", "requesting server list");
NETPACKET packet;
mem_zero(&packet, sizeof(packet));
packet.client_id = -1;
packet.address = master_server;
packet.flags = PACKETFLAG_CONNLESS;
@ -347,9 +353,12 @@ void client_serverbrowse_refresh(int lan)
static void client_serverbrowse_request(int id)
{
if(config.debug)
{
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];
@ -388,7 +397,8 @@ static int state;
int client_state() { return state; }
static void client_set_state(int s)
{
dbg_msg("game", "state change. last=%d current=%d", state, s);
if(config.debug)
dbg_msg("client", "state change. last=%d current=%d", state, s);
int old = state;
state = s;
if(old != s)
@ -422,6 +432,7 @@ void client_connect(const char *server_address_str)
net.connect(&server_address);
client_set_state(CLIENTSTATE_CONNECTING);
current_tick = 0;
}
void client_disconnect()
@ -459,9 +470,10 @@ static void client_debug_render()
static float frametime_avg = 0;
frametime_avg = frametime_avg*0.9f + frametime*0.1f;
char buffer[512];
sprintf(buffer, "send: %6d recv: %6d latency: %4.0f %c gfxmem: %6dk fps: %3d",
sprintf(buffer, "send: %6d recv: %6d snaploss: %4d latency: %4.0f %c gfxmem: %6dk fps: %3d",
(current.send_bytes-prev.send_bytes)*10,
(current.recv_bytes-prev.recv_bytes)*10,
snaploss,
latency*1000.0f, extra_polating?'E':' ',
gfx_memory_usage()/1024,
(int)(1.0f/frametime_avg));
@ -488,7 +500,7 @@ static void client_render()
static void client_error(const char *msg)
{
dbg_msg("game", "error: %s", msg);
dbg_msg("client", "error: %s", msg);
client_send_error(msg);
client_set_state(CLIENTSTATE_QUITING);
}
@ -549,6 +561,7 @@ static void client_process_packet(NETPACKET *packet)
packet->address.ip[0], packet->address.ip[1], packet->address.ip[2],
packet->address.ip[3], packet->address.port);
if(config.debug)
dbg_msg("client", "got server info");
servers.num++;
@ -565,6 +578,7 @@ static void client_process_packet(NETPACKET *packet)
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();
if(config.debug)
dbg_msg("client", "got server info");
break;
}
@ -611,7 +625,7 @@ static void client_process_packet(NETPACKET *packet)
if(msg != NETMSG_SNAPEMPTY)
part_size = msg_unpack_int();
if(snapshot_part == part)
if(snapshot_part == part && game_tick > current_tick)
{
// TODO: clean this up abit
const char *d = (const char *)msg_unpack_raw(part_size);
@ -620,7 +634,38 @@ static void client_process_packet(NETPACKET *packet)
if(snapshot_part == num_parts)
{
current_tick = game_tick;
snapshot_part = 0;
// find snapshot that we should use as delta
static snapshot emptysnap;
emptysnap.data_size = 0;
emptysnap.num_items = 0;
snapshot *deltashot = &emptysnap;
// find delta
if(delta_tick >= 0)
{
//void *delta_data;
snapshot_info *delta_info = client_snapshot_find(delta_tick);
//deltashot_size = snapshots_new.get(delta_tick, 0, &delta_data);
if(delta_info)
deltashot = delta_info->snap;
else
{
// couldn't find the delta snapshots that the server used
// to compress this snapshot. force the server to resync
if(config.debug)
dbg_msg("client", "error, couldn't find the delta snapshot");
// ack snapshot
msg_pack_start_system(NETMSG_SNAPACK, 0);
msg_pack_int(-1);
msg_pack_end();
client_send_msg();
return;
}
}
// decompress snapshot
void *deltadata = snapshot_empty_delta();
@ -636,27 +681,6 @@ static void client_process_packet(NETPACKET *packet)
deltasize = intsize;
}
// find snapshot that we should use as delta
static snapshot emptysnap;
emptysnap.data_size = 0;
emptysnap.num_items = 0;
snapshot *deltashot = &emptysnap;
if(delta_tick >= 0)
{
//void *delta_data;
snapshot_info *delta_info = client_snapshot_find(delta_tick);
//deltashot_size = snapshots_new.get(delta_tick, 0, &delta_data);
if(delta_info)
deltashot = delta_info->snap;
else
{
// TODO: handle this
dbg_msg("client", "error, couldn't find the delta snapshot");
}
}
//dbg_msg("UNPACK", "%d unpacked with %d", game_tick, delta_tick);
unsigned char tmpbuffer3[MAX_SNAPSHOT_SIZE];
@ -681,12 +705,17 @@ static void client_process_packet(NETPACKET *packet)
// apply snapshot, cycle pointers
recived_snapshots++;
if(current_tick > 0)
snaploss += game_tick-current_tick-1;
current_tick = game_tick;
// we got two snapshots until we see us self as connected
if(recived_snapshots <= 2)
{
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
snapshots[SNAP_CURRENT] = snap;
snapshot_start_time = time_get();
}
if(recived_snapshots == 2)
@ -699,6 +728,7 @@ static void client_process_packet(NETPACKET *packet)
int64 t = now - game_tick*time_freq()/50;
if(game_start_time == -1 || t < game_start_time)
{
if(config.debug)
dbg_msg("client", "adjusted time");
game_start_time = t;
}
@ -707,11 +737,6 @@ static void client_process_packet(NETPACKET *packet)
float current_latency = (now-wanted)/(float)time_freq();
latency = latency*0.95f+current_latency*0.05f;
//if(recived_snapshots > 2)
// modc_newsnapshot();
snapshot_part = 0;
// ack snapshot
msg_pack_start_system(NETMSG_SNAPACK, 0);
msg_pack_int(game_tick);
@ -781,15 +806,15 @@ static void client_run(const char *direct_connect_server)
if(!client_load_data())
return;
// init menu
modmenu_init(); // TODO: remove
// init snapshotting
snap_init();
// init the mod
modc_init();
// init menu
modmenu_init(); // TODO: remove
// open socket
NETADDR4 bindaddr;
mem_zero(&bindaddr, sizeof(bindaddr));
@ -837,7 +862,8 @@ static void client_run(const char *direct_connect_server)
{
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
snapshots[SNAP_CURRENT] = next;
snapshot_start_time = t;
if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
modc_newsnapshot();
if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
modc_newsnapshot();
@ -854,12 +880,25 @@ static void client_run(const char *direct_connect_server)
break;
}
}
if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
{
int64 curtick_start = game_start_time + (snapshots[SNAP_CURRENT]->tick+1)*time_freq()/50;
if(latency > 0)
curtick_start += (int64)(time_freq()*(latency*1.1f));
int64 prevtick_start = game_start_time + (snapshots[SNAP_PREV]->tick+1)*time_freq()/50;
if(latency > 0)
prevtick_start += (int64)(time_freq()*(latency*1.1f));
intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start);
}
}
// send input
if(client_state() == CLIENTSTATE_ONLINE)
{
if(server_spam_address)
if(config.stress&1 && client_localtime() > 10.0f)
client_disconnect();
if(input_is_changed || time_get() > last_input+time_freq())
@ -870,8 +909,8 @@ static void client_run(const char *direct_connect_server)
}
}
if(client_state() == CLIENTSTATE_OFFLINE && server_spam_address)
client_connect(server_spam_address);
if(client_state() == CLIENTSTATE_OFFLINE && config.stress && (frames%100) == 0)
client_connect(config.cl_stress_server);
// update input
inp_update();
@ -933,10 +972,19 @@ static void client_run(const char *direct_connect_server)
client_serverbrowse_update();
// render
if(config.stress)
{
if((frames%10) == 0)
{
client_render();
// swap the buffers
gfx_swap();
}
}
else
{
client_render();
gfx_swap();
}
// check conditions
if(client_state() == CLIENTSTATE_QUITING)
@ -947,9 +995,12 @@ static void client_run(const char *direct_connect_server)
thread_sleep(1);
if(reporttime < time_get())
{
if(config.debug)
{
dbg_msg("client/report", "fps=%.02f netstate=%d",
frames/(float)(reportinterval/time_freq()), net.state());
}
frames = 0;
reporttime += reportinterval;
}
@ -977,12 +1028,24 @@ int main(int argc, char **argv)
dbg_msg("client", "starting...");
config_reset();
#ifdef CONF_PLATFORM_MACOSX
config_load("~/.teewars");
const char *config_filename = "~/.teewars";
#else
config_load("default.cfg");
const char *config_filename = "default.cfg";
#endif
for(int i = 1; i < argc; i++)
{
if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1)
{
config_filename = argv[i+1];
i++;
}
}
config_load(config_filename);
const char *direct_connect_server = 0x0;
snd_set_master_volume(config.volume / 255.0f);
bool editor = false;
@ -999,28 +1062,12 @@ int main(int argc, char **argv)
i++;
direct_connect_server = argv[i];
}
else if(argv[i][0] == '-' && argv[i][1] == 's' && argv[i][2] == 0 && argc - i > 1)
{
// -s SERVER:PORT
i++;
server_spam_address = argv[i];
}
else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1)
{
// -n NAME
i++;
config_set_player_name(&config, argv[i]);
}
else if(argv[i][0] == '-' && argv[i][1] == 'w' && argv[i][2] == 0)
{
// -w
config.gfx_fullscreen = 0;
}
else if(argv[i][0] == '-' && argv[i][1] == 'e' && argv[i][2] == 0)
{
editor = true;
}
else
config_set(argv[i]);
}
if(editor)

View file

@ -122,6 +122,12 @@ bool gfx_init()
screen_width = config.gfx_screen_width;
screen_height = config.gfx_screen_height;
if(config.stress)
{
screen_width = 320;
screen_height = 240;
}
if(config.gfx_fullscreen)
{
if(!context.create(screen_width, screen_height, 24, 0, 0, 0, opengl::context::FLAG_FULLSCREEN))
@ -139,6 +145,11 @@ bool gfx_init()
}
}
context.set_title("Teewars");
// We don't want to see the window when we run the stress testing
if(config.stress)
context.iconify();
// Init vertices
if (vertices)
@ -146,7 +157,6 @@ bool gfx_init()
vertices = (custom_vertex*)mem_alloc(sizeof(custom_vertex) * vertex_buffer_size, 1);
num_vertices = 0;
context.set_title("---");
/*
dbg_msg("gfx", "OpenGL version %d.%d.%d", context.version_major(),
@ -250,6 +260,7 @@ int gfx_get_video_modes(video_mode *list, int maxcount)
mem_copy(list, fakemodes, sizeof(fakemodes));
return min((int)(sizeof(fakemodes)/sizeof(video_mode)), maxcount);
}
return context.getvideomodes((opengl::videomode *)list, maxcount);
}
@ -321,6 +332,7 @@ int gfx_load_texture_raw(int w, int h, int format, const void *data)
}
}
if(config.debug)
dbg_msg("gfx", "%d = %dx%d", tex, w, h);
// set data and return
@ -790,25 +802,35 @@ double extra_kerning[256*256] = {0};
pretty_font *current_font = &default_font;
void gfx_pretty_text(float x, float y, float size, const char *text, int max_width)
static int word_length(const char *text)
{
int s = 1;
while(1)
{
if(*text == 0)
return s-1;
if(*text == '\n' || *text == '\t' || *text == ' ')
return s;
text++;
s++;
}
}
float gfx_pretty_text_raw(float x, float y, float size, const char *text_, int length)
{
const unsigned char *text = (unsigned char *)text_;
const float spacing = 0.05f;
gfx_texture_set(current_font->font_texture);
gfx_quads_begin();
float startx = x;
if(length < 0)
length = strlen(text_);
while (*text)
while(length)
{
const int c = *text;
text++;
if(c == '\n')
{
x = startx;
y += size;
}
else
{
const float width = current_font->m_CharEndTable[c] - current_font->m_CharStartTable[c];
x -= size * current_font->m_CharStartTable[c];
@ -822,32 +844,51 @@ void gfx_pretty_text(float x, float y, float size, const char *text, int max_wid
gfx_quads_drawTL(x, y, size, size);
double x_nudge = 0;
if (text[1])
if(length > 1 && text[1])
x_nudge = extra_kerning[text[0] + text[1] * 256];
x += (width + current_font->m_CharStartTable[c] + spacing + x_nudge) * size;
if (max_width != -1 && x - startx > max_width)
{
x = startx;
y += size - 2;
}
}
text++;
length--;
}
gfx_quads_end();
return x;
}
float gfx_pretty_text_width(float size, const char *text, int length)
void gfx_pretty_text(float x, float y, float size, const char *text, int max_width)
{
if(max_width == -1)
gfx_pretty_text_raw(x, y, size, text, -1);
else
{
float startx = x;
while(*text)
{
int wlen = word_length(text);
float w = gfx_pretty_text_width(size, text, wlen);
if(x+w-startx > max_width)
{
y += size-2;
x = startx;
}
x = gfx_pretty_text_raw(x, y, size, text, wlen);
text += wlen;
}
}
}
float gfx_pretty_text_width(float size, const char *text_, int length)
{
const float spacing = 0.05f;
float w = 0.0f;
const unsigned char *text = (unsigned char *)text_;
const char *stop;
const unsigned char *stop;
if (length == -1)
stop = text + strlen(text);
stop = text + strlen((char*)text);
else
stop = text + length;

View file

@ -3,6 +3,7 @@
#include <baselib/stream/file.h>
#include <engine/interface.h>
#include <engine/config.h>
extern "C" {
#include "../../wavpack/wavpack.h"
@ -24,6 +25,16 @@ static const float GLOBAL_SOUND_DELAY = 0.05f;
class sound_data
{
public:
sound_data() :
data(0x0),
num_samples(0),
rate(0),
channels(0),
sustain_start(-1),
sustain_end(-1),
last_played(0)
{ }
short *data;
int num_samples;
int rate;
@ -33,13 +44,14 @@ public:
int64 last_played;
};
inline short clamp(int i)
template<typename T>
inline const T clamp(const T val, const T lower, const T upper)
{
if(i > 0x7fff)
return 0x7fff;
if(i < -0x7fff)
return -0x7fff;
return i;
if(val > upper)
return upper;
if(val < lower)
return lower;
return val;
}
static class mixer : public audio_stream
@ -65,34 +77,40 @@ public:
enum
{
MAX_CHANNELS=32,
MAX_FILL_FRAMES=256,
};
channel channels[MAX_CHANNELS];
int buffer[MAX_FILL_FRAMES*2];
void fill_mono(short *out, unsigned long frames, channel *c, float dv = 0.0f)
void fill_mono(int *out, unsigned long frames, channel *c, float dv = 0.0f)
{
float pl = clamp(1.0f - c->pan, 0.0f, 1.0f);
float pr = clamp(1.0f + c->pan, 0.0f, 1.0f);
for(unsigned long i = 0; i < frames; i++)
{
float p = (1.0f-(c->pan+1.0f)*0.5f);
int val = (int)(p*c->vol * master_volume * c->data->data[c->tick]);
out[i<<1] += (short)val;
out[(i<<1)+1] += (short)val;
float val = c->vol * master_volume * c->data->data[c->tick];
out[i<<1] += (int)(pl*val);
out[(i<<1)+1] += (int)(pr*val);
c->tick++;
c->vol += dv;
if(c->vol < 0.0f) c->vol = 0.0f;
}
}
void fill_stereo(short *out, unsigned long frames, channel *c, float dv = 0.0f)
void fill_stereo(int *out, unsigned long frames, channel *c, float dv = 0.0f)
{
float pl = clamp(1.0f - c->pan, 0.0f, 1.0f);
float pr = clamp(1.0f + c->pan, 0.0f, 1.0f);
for(unsigned long i = 0; i < frames; i++)
{
float pl = c->pan<0.0f?-c->pan:1.0f;
float pr = c->pan>0.0f?1.0f-c->pan:1.0f;
int vl = (int)(pl*c->vol * master_volume * c->data->data[c->tick]);
int vr = (int)(pr*c->vol * master_volume * c->data->data[c->tick + 1]);
out[i<<1] += (short)vl;
out[(i<<1)+1] += (short)vr;
out[i<<1] += vl;
out[(i<<1)+1] += vr;
c->tick += 2;
c->vol += dv;
if(c->vol < 0.0f) c->vol = 0.0f;
@ -103,10 +121,12 @@ public:
{
short *out = (short*)output;
dbg_assert(frames <= MAX_FILL_FRAMES, "not enough fill frames in buffer");
for(unsigned long i = 0; i < frames; i++)
{
out[i<<1] = 0;
out[(i<<1)+1] = 0;
buffer[i<<1] = 0;
buffer[(i<<1)+1] = 0;
}
for(int c = 0; c < MAX_CHANNELS; c++)
@ -133,9 +153,9 @@ public:
}
if(channels[c].data->channels == 1)
fill_mono(out, to_fill, &channels[c], dv);
fill_mono(buffer, to_fill, &channels[c], dv);
else
fill_stereo(out, to_fill, &channels[c], dv);
fill_stereo(buffer, to_fill, &channels[c], dv);
if(channels[c].loop >= 0 &&
channels[c].data->sustain_start >= 0 &&
@ -156,6 +176,12 @@ public:
filled += to_fill;
}
}
for(unsigned long i = 0; i < frames; i++)
{
out[i<<1] = (short)clamp(buffer[i<<1], -0x7fff, 0x7fff);
out[(i<<1)+1] = (short)clamp(buffer[(i<<1)+1], -0x7fff, 0x7fff);
}
}
int play(sound_data *sound, unsigned loop, float vol, float pan)
@ -262,7 +288,7 @@ int snd_load_wv(const char *filename)
char error[100];
file = fopen(filename, "r");
file = fopen(filename, "rb");
WavpackContext *context = WavpackOpenFileInput(read_data, error);
if (context)
@ -453,6 +479,7 @@ int snd_load_wav(const char *filename)
{
unsigned char smpl[36];
unsigned char loop[24];
if(config.debug)
dbg_msg("sound/wav", "got sustain");
file.read(smpl, sizeof(smpl));
@ -482,7 +509,10 @@ int snd_load_wav(const char *filename)
}
if(id >= 0)
{
if(config.debug)
dbg_msg("sound/wav", "loaded %s", filename);
}
else
dbg_msg("sound/wav", "failed to load %s", filename);

View file

@ -32,7 +32,7 @@ const unsigned char *vint_unpack(const unsigned char *src, int *i)
int sign = (*src>>6)&1;
*i = *src&0x3F;
while(1)
do
{
if(!(*src&0x80)) break;
src++;
@ -49,7 +49,7 @@ const unsigned char *vint_unpack(const unsigned char *src, int *i)
if(!(*src&0x80)) break;
src++;
*i |= (*src&(0x7F))<<(6+7+7+7);
}
} while(0);
src++;
*i ^= -sign; // if(sign) *i = ~(*i)

View file

@ -119,7 +119,7 @@ void config_save(const char *filename)
#else
const char newline[] = "\n";
#endif
const int newline_len = sizeof(newline);
const int newline_len = sizeof(newline)-1;
#define MACRO_CONFIG_INT(name,def,min,max) { char str[256]; sprintf(str, "%s=%i", #name, config.name); file.write(str, strlen(str)); file.write(newline, newline_len); }
#define MACRO_CONFIG_STR(name,len,def) { file.write(#name, strlen(#name)); file.write("=", 1); file.write(config.name, strlen(config.name)); file.write(newline, newline_len); }

View file

@ -1,12 +1,16 @@
#include "../game/game_variables.h"
MACRO_CONFIG_INT(debug, 0, 0, 1)
MACRO_CONFIG_INT(volume, 200, 0, 255)
MACRO_CONFIG_INT(cpu_throttle, 0, 0, 1)
MACRO_CONFIG_STR(player_name, 32, "nameless tee")
MACRO_CONFIG_STR(clan_name, 32, "")
MACRO_CONFIG_STR(password, 32, "")
MACRO_CONFIG_INT(debug, 0, 0, 1)
MACRO_CONFIG_INT(stress, 0, 0, 0)
MACRO_CONFIG_STR(cl_stress_server, 32, "localhost")
MACRO_CONFIG_INT(gfx_screen_width, 800, 0, 0)
MACRO_CONFIG_INT(gfx_screen_height, 600, 0, 0)
MACRO_CONFIG_INT(gfx_fullscreen, 1, 0, 1)
@ -25,3 +29,7 @@ MACRO_CONFIG_STR(sv_name, 128, "unnamed server")
MACRO_CONFIG_STR(sv_bindaddr, 128, "")
MACRO_CONFIG_INT(sv_port, 8303, 0, 0)
MACRO_CONFIG_INT(sv_sendheartbeats, 1, 0, 1)
MACRO_CONFIG_STR(sv_map, 128, "dm1")
MACRO_CONFIG_INT(sv_max_clients, 8, 1, 8)

View file

@ -12,15 +12,11 @@ enum
{
MAX_CLIENTS=8,
SERVER_TICK_SPEED=50,
SERVER_CLIENT_TIMEOUT=5,
SNAP_CURRENT=0,
SNAP_PREV=1,
IMG_RGB=0,
IMG_RGBA=1,
/*
IMG_BGR,
IMG_BGRA,*/
CLIENTSTATE_OFFLINE=0,
CLIENTSTATE_CONNECTING,
@ -795,4 +791,7 @@ void client_quit();
void client_serverbrowse_refresh(int lan);
int client_serverbrowse_getlist(server_info **servers);
int snap_new_id();
void snap_free_id(int id);
#endif

View file

@ -1,6 +1,8 @@
#include <baselib/system.h>
#include <string.h>
#include <stdio.h>
#include "config.h"
#include "network.h"
#include "ringbuffer.h"
@ -39,6 +41,8 @@ enum
NETWORK_PACKETFLAG_VITAL=0x08,
NETWORK_PACKETFLAG_RESEND=0x10,
NETWORK_PACKETFLAG_CONNLESS=0x20,
NETWORK_MAX_SEQACK=0x1000,
};
static int current_token = 1;
@ -75,19 +79,23 @@ static void send_packet(NETSOCKET socket, NETADDR4 *addr, NETPACKETDATA *packet)
struct NETCONNECTION
{
unsigned seq;
unsigned ack;
unsigned short seq;
unsigned short ack;
unsigned state;
int token;
int remote_closed;
int connected;
int disconnected;
ring_buffer buffer;
int64 last_update_time;
int64 last_recv_time;
int64 last_send_time;
char error_string[256];
NETADDR4 peeraddr;
@ -104,6 +112,10 @@ struct NETSERVER
{
NETSOCKET socket;
NETSLOT slots[NETWORK_MAX_CLIENTS];
int max_clients;
NETFUNC_NEWCLIENT new_client;
NETFUNC_NEWCLIENT del_client;
void *user_ptr;
unsigned char recv_buffer[NETWORK_MAX_PACKET_SIZE];
};
@ -125,6 +137,7 @@ static void conn_reset(NETCONNECTION *conn)
{
conn->seq = 0;
conn->ack = 0;
conn->remote_closed = 0;
//dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_OFFLINE);
if(conn->state == NETWORK_CONNSTATE_ONLINE ||
@ -136,6 +149,7 @@ static void conn_reset(NETCONNECTION *conn)
conn->state = NETWORK_CONNSTATE_OFFLINE;
conn->last_send_time = 0;
conn->last_recv_time = 0;
conn->last_update_time = 0;
conn->token = -1;
conn->buffer.reset();
}
@ -176,7 +190,7 @@ static void conn_ack(NETCONNECTION *conn, int ack)
break;
NETPACKETDATA *resend = (NETPACKETDATA *)i->data();
if(resend->seq <= ack)
if(resend->seq <= ack || (ack < NETWORK_MAX_SEQACK/3 && resend->seq > NETWORK_MAX_SEQACK/2))
conn->buffer.pop_first();
else
break;
@ -207,7 +221,9 @@ static void conn_resend(NETCONNECTION *conn)
static void conn_send(NETCONNECTION *conn, int flags, int data_size, const void *data)
{
if(flags&NETWORK_PACKETFLAG_VITAL)
conn->seq++;
{
conn->seq = (conn->seq+1)%NETWORK_MAX_SEQACK;
}
NETPACKETDATA p;
p.ID[0] = 'T';
@ -244,6 +260,7 @@ static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr)
conn_reset(conn);
conn->peeraddr = *addr;
conn->token = current_token++;
mem_zero(conn->error_string, sizeof(conn->error_string));
//dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_CONNECT);
conn->state = NETWORK_CONNSTATE_CONNECT;
conn_send(conn, NETWORK_PACKETFLAG_CONNECT, 0, 0);
@ -252,10 +269,13 @@ static int conn_connect(NETCONNECTION *conn, NETADDR4 *addr)
static void conn_disconnect(NETCONNECTION *conn, const char *reason)
{
if(conn->remote_closed == 0)
{
if(reason)
conn_send(conn, NETWORK_PACKETFLAG_CLOSE, strlen(reason)+1, reason);
else
conn_send(conn, NETWORK_PACKETFLAG_CLOSE, 0, 0);
}
conn_reset(conn);
}
@ -267,11 +287,15 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
if(p->flags&NETWORK_PACKETFLAG_CLOSE)
{
conn_reset(conn);
conn->state = NETWORK_CONNSTATE_ERROR;
conn->remote_closed = 1;
//conn_reset(conn);
if(p->data_size)
conn_set_error(conn, (char *)p->data);
else
conn_set_error(conn, "no reason given");
if(config.debug)
dbg_msg("conn", "closed reason='%s'", conn_error(conn));
return 0;
}
@ -288,6 +312,7 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
conn->token = p->token;
//dbg_msg("connection", "token set to %d", p->token);
conn_send(conn, NETWORK_PACKETFLAG_CONNECT|NETWORK_PACKETFLAG_ACCEPT, 0, 0);
if(config.debug)
dbg_msg("connection", "got connection, sending connect+accept");
}
}
@ -310,15 +335,15 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
if(p->flags&NETWORK_PACKETFLAG_VITAL)
{
if(p->seq == conn->ack+1)
if(p->seq == (conn->ack+1)%NETWORK_MAX_SEQACK)
{
// in sequence
conn->ack++;
conn->ack = (conn->ack+1)%NETWORK_MAX_SEQACK;
}
else
{
// out of sequence, request resend
//dbg_msg("conn", "asking for resend");
dbg_msg("conn", "asking for resend %d %d", p->seq, (conn->ack+1)%NETWORK_MAX_SEQACK);
conn_send(conn, NETWORK_PACKETFLAG_RESEND, 0, 0);
return 0;
}
@ -361,8 +386,10 @@ static int conn_feed(NETCONNECTION *conn, NETPACKETDATA *p, NETADDR4 *addr)
}*/
else
{
conn_reset(conn);
//conn_reset(conn);
// strange packet, wrong state
conn->state = NETWORK_CONNSTATE_ERROR;
conn_set_error(conn, "strange state and packet");
}
}
else
@ -380,14 +407,36 @@ static int conn_update(NETCONNECTION *conn)
if(conn->state == NETWORK_CONNSTATE_OFFLINE || conn->state == NETWORK_CONNSTATE_ERROR)
return 0;
// watch out for major hitches
int64 now = time_get();
int64 delta = now-conn->last_update_time;
if(conn->last_update_time && delta > time_freq()/2)
{
dbg_msg("conn", "hitch %d", (int)((delta*1000)/time_freq()));
conn->last_recv_time += delta;
ring_buffer::item *i = conn->buffer.first();
while(i)
{
NETPACKETDATA *resend = (NETPACKETDATA *)i->data();
resend->first_send_time += delta;
i = i->next;
}
}
conn->last_update_time = now;
// check for timeout
if(conn->state != NETWORK_CONNSTATE_OFFLINE &&
conn->state != NETWORK_CONNSTATE_CONNECT &&
(time_get()-conn->last_recv_time) > time_freq()*3)
(now-conn->last_recv_time) > time_freq()*10)
{
//dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_ERROR);
conn->state = NETWORK_CONNSTATE_ERROR;
conn_set_error(conn, "timeout");
char buf[128];
sprintf(buf, "timeout %lld %lld %lld %lld", now-conn->last_recv_time, now, conn->last_recv_time, time_freq()*10);
conn_set_error(conn, buf);
}
// check for large buffer errors
@ -401,11 +450,11 @@ static int conn_update(NETCONNECTION *conn)
if(conn->buffer.first())
{
NETPACKETDATA *resend = (NETPACKETDATA *)conn->buffer.first()->data();
if(time_get()-resend->first_send_time > time_freq()*3)
if(now-resend->first_send_time > time_freq()*10)
{
//dbg_msg("connection", "state = %d->%d", conn->state, NETWORK_CONNSTATE_ERROR);
conn->state = NETWORK_CONNSTATE_ERROR;
conn_set_error(conn, "too weak connection (not acked for 3 seconds)");
conn_set_error(conn, "too weak connection (not acked for 10 seconds)");
}
}
@ -463,9 +512,18 @@ static int check_packet(unsigned char *buffer, int size, NETPACKETDATA *packet)
NETSERVER *net_server_open(NETADDR4 bindaddr, int max_clients, int flags)
{
NETSOCKET socket = net_udp4_create(bindaddr);
if(socket == NETSOCKET_INVALID)
return 0;
NETSERVER *server = (NETSERVER *)mem_alloc(sizeof(NETSERVER), 1);
mem_zero(server, sizeof(NETSERVER));
server->socket = net_udp4_create(bindaddr);
server->socket = socket;
server->max_clients = max_clients;
if(server->max_clients > NETWORK_MAX_CLIENTS)
server->max_clients = NETWORK_MAX_CLIENTS;
if(server->max_clients < 1)
server->max_clients = 1;
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
conn_init(&server->slots[i].conn, server->socket);
@ -473,15 +531,24 @@ NETSERVER *net_server_open(NETADDR4 bindaddr, int max_clients, int flags)
return server;
}
int net_server_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 net_server_close(NETSERVER *s)
{
// TODO: implement me
return 0;
}
/*
int net_server_newclient(NETSERVER *s)
{
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
for(int i = 0; i < s->max_clients; i++)
{
if(s->slots[i].conn.connected)
{
@ -495,7 +562,7 @@ int net_server_newclient(NETSERVER *s)
int net_server_delclient(NETSERVER *s)
{
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
for(int i = 0; i < s->max_clients; i++)
{
if(s->slots[i].conn.disconnected)
{
@ -505,20 +572,24 @@ int net_server_delclient(NETSERVER *s)
}
return -1;
}
}*/
int net_server_drop(NETSERVER *s, int client_id, const char *reason)
{
// TODO: insert lots of checks here
dbg_msg("net_server", "client dropped. cid=%d reason=\"%s\"", client_id, reason);
conn_disconnect(&s->slots[client_id].conn, reason);
if(s->del_client)
s->del_client(client_id, s->user_ptr);
//conn_reset(&s->slots[client_id].conn);
return 0;
}
int net_server_update(NETSERVER *s)
{
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
for(int i = 0; i < s->max_clients; i++)
{
conn_update(&s->slots[i].conn);
if(s->slots[i].conn.state == NETWORK_CONNSTATE_ERROR)
@ -560,7 +631,7 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet)
int found = 0;
// check if we already got this client
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
for(int i = 0; i < s->max_clients; i++)
{
if(s->slots[i].conn.state != NETWORK_CONNSTATE_OFFLINE &&
net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
@ -574,27 +645,42 @@ int net_server_recv(NETSERVER *s, NETPACKET *packet)
// client that wants to connect
if(!found)
{
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
for(int i = 0; i < s->max_clients; i++)
{
if(s->slots[i].conn.state == NETWORK_CONNSTATE_OFFLINE)
{
//dbg_msg("netserver", "connection started %d", i);
conn_feed(&s->slots[i].conn, &data, &addr);
found = 1;
conn_feed(&s->slots[i].conn, &data, &addr);
if(s->new_client)
s->new_client(i, s->user_ptr);
break;
}
}
}
if(found)
if(!found)
{
// TODO: send error
// send connectionless packet
const char errstring[] = "server full";
NETPACKETDATA p;
p.ID[0] = 'T';
p.ID[1] = 'W';
p.version = NETWORK_VERSION;
p.flags = NETWORK_PACKETFLAG_CLOSE;
p.seq = 0;
p.ack = 0;
p.crc = 0;
p.token = data.token;
p.data_size = sizeof(errstring);
p.data = (unsigned char *)errstring;
send_packet(s->socket, &addr, &p);
}
}
else
{
// find matching slot
for(int i = 0; i < NETWORK_MAX_CLIENTS; i++)
for(int i = 0; i < s->max_clients; i++)
{
if(net_addr4_cmp(&s->slots[i].conn.peeraddr, &addr) == 0)
{
@ -648,7 +734,7 @@ int net_server_send(NETSERVER *s, NETPACKET *packet)
else
{
dbg_assert(packet->client_id >= 0, "errornous client id");
dbg_assert(packet->client_id < NETWORK_MAX_CLIENTS, "errornous client id");
dbg_assert(packet->client_id < s->max_clients, "errornous client id");
int flags = 0;
if(packet->flags&PACKETFLAG_VITAL)
flags |= NETWORK_PACKETFLAG_VITAL;
@ -664,7 +750,7 @@ void net_server_stats(NETSERVER *s, NETSTATS *stats)
int num_stats = sizeof(NETSTATS)/sizeof(int);
int *istats = (int *)stats;
for(int c = 0; c < NETWORK_MAX_CLIENTS; c++)
for(int c = 0; c < s->max_clients; c++)
{
int *sstats = (int *)(&(s->slots[c].conn.stats));
for(int i = 0; i < num_stats; i++)
@ -774,6 +860,7 @@ int net_client_send(NETCLIENT *c, NETPACKET *packet)
p.seq = 0;
p.ack = 0;
p.crc = 0;
p.token = 0;
p.data_size = packet->data_size;
p.data = (unsigned char *)packet->data;
send_packet(c->socket, &packet->address, &p);

View file

@ -35,15 +35,19 @@ enum
NETSTATE_ONLINE,
};
typedef int (*NETFUNC_DELCLIENT)(int cid, void *user);
typedef int (*NETFUNC_NEWCLIENT)(int cid, void *user);
// server side
NETSERVER *net_server_open(NETADDR4 bindaddr, int max_clients, int flags);
int net_server_set_callbacks(NETSERVER *s, NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user);
int net_server_recv(NETSERVER *s, NETPACKET *packet);
int net_server_send(NETSERVER *s, NETPACKET *packet);
int net_server_close(NETSERVER *s);
int net_server_update(NETSERVER *s);
int net_server_drop(NETSERVER *s, int client_id, const char *reason);
int net_server_newclient(NETSERVER *s); // -1 when no more, else, client id
int net_server_delclient(NETSERVER *s); // -1 when no more, else, client id
//int net_server_newclient(NETSERVER *s); // -1 when no more, else, client id
//int net_server_delclient(NETSERVER *s); // -1 when no more, else, client id
void net_server_stats(NETSERVER *s, NETSTATS *stats);
// client side
@ -71,13 +75,16 @@ public:
int open(NETADDR4 bindaddr, int max, int flags) { ptr = net_server_open(bindaddr, max, flags); return ptr != 0; }
int close() { int r = net_server_close(ptr); ptr = 0; return r; }
int set_callbacks(NETFUNC_NEWCLIENT new_client, NETFUNC_DELCLIENT del_client, void *user)
{ return net_server_set_callbacks(ptr, new_client, del_client, user); }
int recv(NETPACKET *packet) { return net_server_recv(ptr, packet); }
int send(NETPACKET *packet) { return net_server_send(ptr, packet); }
int update() { return net_server_update(ptr); }
int drop(int client_id, const char *reason) { return net_server_drop(ptr, client_id, reason); }
int newclient() { return net_server_newclient(ptr); }
int delclient() { return net_server_delclient(ptr); }
//int newclient() { return net_server_newclient(ptr); }
//int delclient() { return net_server_delclient(ptr); }
void stats(NETSTATS *stats) { net_server_stats(ptr, stats); }
};

View file

@ -1,4 +1,5 @@
#include <stdarg.h>
#include <string.h>
#include <baselib/stream/file.h>
#include <baselib/network.h>
@ -273,6 +274,9 @@ public:
int get_int()
{
if(current >= end)
return 0;
int i;
current = vint_unpack(current, &i);
// TODO: might be changed into variable width
@ -285,6 +289,9 @@ public:
const char *get_string()
{
if(current >= end)
return "";
// TODO: add range check
// TODO: add debug marker
const char *ptr = (const char *)current;

View file

@ -30,6 +30,97 @@ void *snap_new_item(int type, int id, int size)
return builder.new_item(type, id, size);
}
struct snap_id
{
short next;
short state; // 0 = free, 1 = alloced, 2 = timed
int timeout_tick;
};
static const int MAX_IDS = 8*1024; // should be lowered
static snap_id snap_ids[8*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;
void snap_init_id()
{
for(int 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;
}
int snap_new_id()
{
dbg_assert(snap_id_inited == 1, "requesting id too soon");
// process timed ids
while(snap_first_timed_id != -1 && snap_ids[snap_first_timed_id].timeout_tick < server_tick())
{
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 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_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_tick = server_tick() + server_tickspeed()*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;
}
}
//
class client
{
@ -133,30 +224,50 @@ int server_send_msg(int client_id)
return 0;
}
static int new_client_callback(int cid, void *user)
{
clients[cid].state = client::STATE_CONNECTING;
clients[cid].name[0] = 0;
clients[cid].clan[0] = 0;
clients[cid].snapshots.purge_all();
clients[cid].last_acked_snapshot = -1;
return 0;
}
static int del_client_callback(int cid, void *user)
{
clients[cid].state = client::STATE_EMPTY;
clients[cid].name[0] = 0;
clients[cid].clan[0] = 0;
clients[cid].snapshots.purge_all();
mods_client_drop(cid);
return 0;
}
// TODO: remove this class
class server
{
public:
//socket_udp4 game_socket;
const char *map_name;
int64 lasttick;
int64 lastheartbeat;
netaddr4 master_server;
int biggest_snapshot;
bool run(const char *mapname)
bool run()
{
biggest_snapshot = 0;
net_init(); // For Windows compatibility.
map_name = mapname;
snap_init_id();
// load map
if(!map_load(mapname))
if(!map_load(config.sv_map))
{
dbg_msg("server", "failed to load map. mapname='%s'", mapname);
dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
return false;
}
@ -173,12 +284,14 @@ public:
bindaddr.port = config.sv_port;
}
if(!net.open(bindaddr, 0, 0))
if(!net.open(bindaddr, config.sv_max_clients, 0))
{
dbg_msg("network/server", "couldn't open socket");
dbg_msg("server", "couldn't open socket. port might already be in use");
return false;
}
net.set_callbacks(new_client_callback, del_client_callback, 0);
dbg_msg("server", "server name is '%s'", config.sv_name);
dbg_msg("server", "masterserver is '%s'", config.masterserver);
if (net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0)
@ -242,12 +355,16 @@ public:
if(reporttime < time_get())
{
dbg_msg("server/report", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%%",
if(config.debug)
{
dbg_msg("server", "sim=%.02fms snap=%.02fms net=%.02fms total=%.02fms load=%.02f%% ids=%d/%d",
(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);
(totaltime)/reportinterval/(double)time_freq()*100.0f,
snap_id_inusage, snap_id_usage);
}
simulationtime = 0;
snaptime = 0;
@ -313,8 +430,6 @@ public:
delta_tick = clients[i].last_acked_snapshot;
deltashot = (snapshot *)delta_data;
}
else
dbg_msg("server", "no delta, sending full snapshot");
}
// create delta
@ -337,6 +452,7 @@ public:
const int max_size = MAX_SNAPSHOT_PACKSIZE;
int numpackets = (snapshot_size+max_size-1)/max_size;
(void)numpackets;
for(int n = 0, left = snapshot_size; left; n++)
{
int chunk = left < max_size ? left : max_size;
@ -373,14 +489,13 @@ public:
void send_map(int cid)
{
msg_pack_start_system(NETMSG_MAP, MSGFLAG_VITAL);
msg_pack_string(map_name, 0);
msg_pack_string(config.sv_map, 0);
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;
@ -397,7 +512,7 @@ public:
clients[cid].state = client::STATE_EMPTY;
mods_client_drop(cid);
dbg_msg("game", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name);
dbg_msg("server", "player dropped. reason='%s' cid=%x name='%s'", reason, cid, clients[cid].name);
}
void process_client_packet(NETPACKET *packet)
@ -434,7 +549,7 @@ public:
{
if(clients[cid].state != client::STATE_INGAME)
{
dbg_msg("game", "player as entered the game. cid=%x", cid);
dbg_msg("server", "player as entered the game. cid=%x", cid);
clients[cid].state = client::STATE_INGAME;
mods_client_enter(cid);
}
@ -483,7 +598,7 @@ public:
packer.reset();
packer.add_raw(SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
packer.add_string(config.sv_name, 128);
packer.add_string(map_name, 128);
packer.add_string(config.sv_map, 128);
packer.add_int(MAX_CLIENTS); // max_players
int c = 0;
for(int i = 0; i < MAX_CLIENTS; i++)
@ -537,6 +652,7 @@ public:
else if(packet.data_size == sizeof(SERVERBROWSE_FWOK) &&
memcmp(packet.data, SERVERBROWSE_FWOK, sizeof(SERVERBROWSE_FWOK)) == 0)
{
if(config.debug)
dbg_msg("server", "no firewall/nat problems detected");
}
else if(packet.data_size == sizeof(SERVERBROWSE_FWERROR) &&
@ -549,35 +665,6 @@ public:
else
process_client_packet(&packet);
}
// check for removed clients
while(1)
{
int cid = net.delclient();
if(cid == -1)
break;
clients[cid].state = client::STATE_EMPTY;
clients[cid].name[0] = 0;
clients[cid].clan[0] = 0;
clients[cid].snapshots.purge_all();
mods_client_drop(cid);
}
// check for new clients
while(1)
{
int cid = net.newclient();
if(cid == -1)
break;
clients[cid].state = client::STATE_CONNECTING;
clients[cid].name[0] = 0;
clients[cid].clan[0] = 0;
clients[cid].snapshots.purge_all();
clients[cid].last_acked_snapshot = -1;
}
}
};
@ -586,50 +673,30 @@ int main(int argc, char **argv)
dbg_msg("server", "starting...");
config_reset();
#ifdef CONF_PLATFORM_MACOSX
config_load("~/.teewars");
const char *config_filename = "~/.teewars";
#else
config_load("default.cfg");
const char *config_filename = "default.cfg";
#endif
const char *mapname = "dm1";
for(int i = 1; i < argc; i++)
{
if(argv[i][0] == '-' && argv[i][1] == 'f' && argv[i][2] == 0 && argc - i > 1)
{
config_filename = argv[i+1];
i++;
}
}
config_load(config_filename);
// parse arguments
for(int i = 1; i < argc; i++)
{
if(argv[i][0] == '-' && argv[i][1] == 'm' && argv[i][2] == 0 && argc - i > 1)
{
// -m map
i++;
mapname = argv[i];
}
else if(argv[i][0] == '-' && argv[i][1] == 'n' && argv[i][2] == 0 && argc - i > 1)
{
// -n server name
i++;
config_set_sv_name(&config, argv[i]);
}
else if(argv[i][0] == '-' && argv[i][1] == 'p' && argv[i][2] == 0)
{
// -p (private server)
config_set_sv_sendheartbeats(&config, 0);
}
else if(argv[i][0] == '-' && argv[i][1] == 'o' && argv[i][2] == 0)
{
// -o port
i++;
config_set_sv_port(&config, atol(argv[i]));
}
}
if(!mapname)
{
dbg_msg("server", "no map given (-m MAPNAME)");
return 0;
}
config_set(argv[i]);
server_init();
server s;
s.run(mapname);
s.run();
return 0;
}

View file

@ -42,6 +42,19 @@ int snapshot_crc(snapshot *snap)
return crc;
}
void snapshot_debug_dump(snapshot *snap)
{
dbg_msg("snapshot", "data_size=%d num_items=%d", snap->data_size, snap->num_items);
for(int i = 0; i < snap->num_items; i++)
{
snapshot::item *item = snap->get_item(i);
int size = snap->get_item_datasize(i);
dbg_msg("snapshot", "\ttype=%d id=%d", item->type(), item->id());
for(int b = 0; b < size/4; b++)
dbg_msg("snapshot", "\t\t%3d %12d\t%08x", b, item->data()[b], item->data()[b]);
}
}
static int diff_item(int *past, int *current, int *out, int size)
{
/*

View file

@ -42,6 +42,7 @@ struct snapshot
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);

View file

@ -61,10 +61,10 @@ void snd_play_random(int setid, float vol, float pan)
}
// sound volume tweak
static const float stereo_separation = 0.01f;
static const float stereo_separation_deadzone = 512.0f;
static const float volume_distance_falloff = 100.0f;
static const float volume_distance_deadzone = 512.0f;
static const float stereo_separation = 0.001f;
static const float stereo_separation_deadzone = 200.0f;
static const float volume_distance_falloff = 200.0f;
static const float volume_distance_deadzone = 320.0f;
static const float volume_gun = 0.5f;
static const float volume_tee = 0.5f;
static const float volume_hit = 0.5f;
@ -434,16 +434,59 @@ static int killmsg_current = 0;
extern unsigned char internal_data[];
extern void draw_round_rect(float x, float y, float w, float h, float r);
extern int render_popup(const char *caption, const char *text, const char *button_text);
static void render_loading(float percent)
{
gfx_clear(0.65f,0.78f,0.9f);
gfx_mapscreen(0,0,800.0f,600.0f);
float tw;
float w = 700;
float h = 200;
float x = 800/2-w/2;
float y = 600/2-h/2;
gfx_blend_normal();
gfx_texture_set(-1);
gfx_quads_begin();
gfx_quads_setcolor(0,0,0,0.50f);
draw_round_rect(x, y, w, h, 40.0f);
gfx_quads_end();
const char *caption = "Loading";
tw = gfx_pretty_text_width(48.0f, caption);
ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f);
gfx_texture_set(-1);
gfx_quads_begin();
gfx_quads_setcolor(1,1,1,1.0f);
draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f);
gfx_quads_end();
gfx_swap();
}
void modc_init()
{
// load the data container
data = load_data_from_memory(internal_data);
// TODO: should be removed
music_menu = snd_load_wav("data/audio/menu_music.wav");
music_menu = snd_load_wav("data/audio/music_menu.wav");
float total = data->num_sounds+data->num_images;
float current = 0;
// load sounds
for(int s = 0; s < data->num_sounds; s++)
{
render_loading(current/total);
for(int i = 0; i < data->sounds[s].num_sounds; i++)
{
int id;
@ -455,9 +498,16 @@ void modc_init()
data->sounds[s].sounds[i].id = id;
}
current++;
}
// load textures
for(int i = 0; i < data->num_images; i++)
{
render_loading(current/total);
data->images[i].id = gfx_load_texture(data->images[i].filename);
current++;
}
}
void modc_entergame()
@ -483,13 +533,15 @@ void modc_shutdown()
{
}
void modc_newsnapshot()
static bool must_process_events = false;
static void process_events(int s)
{
int num = snap_num_items(SNAP_CURRENT);
int num = snap_num_items(s);
for(int i = 0; i < num; i++)
{
snap_item item;
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
const void *data = snap_get_item(s, i, &item);
if(item.type == EVENT_DAMAGEINDICATION)
{
@ -618,6 +670,26 @@ void modc_newsnapshot()
}
}
}
must_process_events = false;
}
void modc_newsnapshot()
{
if(must_process_events)
process_events(SNAP_PREV);
must_process_events = true;
if(config.stress)
{
if((client_tick()%250) == 0)
{
msg_pack_start(MSG_SAY, MSGFLAG_VITAL);
msg_pack_string("galenskap!!!!", 512);
msg_pack_end();
client_send_msg();
}
}
}
static void render_projectile(const obj_projectile *prev, const obj_projectile *current, int itemid)
@ -883,7 +955,7 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, shift*4);
break;
}
int h = emote == EMOTE_BLINK ? basesize/3 : basesize;
int h = emote == EMOTE_BLINK ? (int)(basesize/3) : (int)(basesize);
gfx_quads_draw(position.x-4+direction.x*4, position.y-8+direction.y*3, basesize, h);
gfx_quads_draw(position.x+4+direction.x*4, position.y-8+direction.y*3, -basesize, h);
}
@ -956,6 +1028,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
{
if(player->health < 0) // dont render dead players
return;
int skin = gametype == GAMETYPE_TDM ? skinseed + player->team : player->clientid;
vec2 direction = get_direction(player->angle);
@ -1248,7 +1321,7 @@ void render_game()
int c = input::last_char(); // TODO: bypasses the engine interface
int k = input::last_key(); // TODO: bypasses the engine interface
if (c >= 32 && c < 255)
if (!(c >= 0 && c < 32))
{
if (chat_input_len < sizeof(chat_input) - 1)
{
@ -1292,12 +1365,8 @@ void render_game()
player_input input;
mem_zero(&input, sizeof(input));
float a = atan((float)mouse_pos.y/(float)mouse_pos.x);
if(mouse_pos.x < 0)
a = a+pi;
input.target_x = (int)mouse_pos.x; //(int)(a*256.0f);
input.target_y = (int)mouse_pos.y; //(int)(a*256.0f);
input.target_x = (int)mouse_pos.x;
input.target_y = (int)mouse_pos.y;
input.activeweapon = -1;
if(chat_active)
@ -1313,25 +1382,30 @@ void render_game()
input.fire = inp_key_pressed(config.key_fire);
input.hook = inp_key_pressed(config.key_hook);
input.blink = inp_key_pressed('S');
//input.blink = inp_key_pressed('S');
// Weapon switching
#define TEST_WEAPON_KEY(key) if (inp_key_pressed(config.key_weapon ## key)) input.activeweapon = key-1;
if(config.scroll_weapon)
{
int delta = inp_mouse_scroll();
input.activeweapon = input.activeweapon + delta;
if(input.activeweapon > 3)
input.activeweapon = 3;
else if(input.activeweapon < 0)
input.activeweapon = 0;
if(inp_key_pressed(config.key_weapon1)) input.activeweapon = 0;
if(inp_key_pressed(config.key_weapon2)) input.activeweapon = 1;
if(inp_key_pressed(config.key_weapon3)) input.activeweapon = 2;
if(inp_key_pressed(config.key_weapon4)) input.activeweapon = 3;
}
TEST_WEAPON_KEY(1);
TEST_WEAPON_KEY(2);
TEST_WEAPON_KEY(3);
TEST_WEAPON_KEY(4);
// stress testing
if(config.stress)
{
float t = client_localtime();
mem_zero(&input, sizeof(input));
input.left = 1;
input.jump = ((int)t)&1;
input.fire = ((int)(t*10))&1;
input.hook = ((int)t)&1;
input.activeweapon = ((int)t)%NUM_WEAPONS;
input.target_x = (int)(sinf(t*3)*100.0f);
input.target_y = (int)(cosf(t*3)*100.0f);
//input.target_x = (int)((rand()/(float)RAND_MAX)*64-32);
//input.target_y = (int)((rand()/(float)RAND_MAX)*64-32);
}
snap_input(&input, sizeof(input));
@ -1366,6 +1440,10 @@ void render_game()
}
}
// everything updated, do events
if(must_process_events)
process_events(SNAP_PREV);
// pseudo format
float zoom = 3.0f;
@ -1413,7 +1491,7 @@ void render_game()
{
float parallax_amount = 0.55f;
select_sprite(cloud_sprites[i]);
draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 1700.0f))+screen_x*parallax_amount,
draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 3000.0f))+screen_x*parallax_amount,
cloud_pos[i].y+screen_y*parallax_amount, 300);
}
gfx_quads_end();
@ -1707,7 +1785,7 @@ void render_game()
// sort players
for(int k = 0; k < num_players; k++) // ffs, bubblesort
{
for(int i = k; i < num_players-1; i++)
for(int i = 0; i < num_players-k-1; i++)
{
if(players[i]->score < players[i+1]->score)
{

View file

@ -414,7 +414,7 @@ int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int
if (at_index > len)
at_index = len;
if (c >= 32 && c < 128)
if (!(c >= 0 && c < 32))
{
if (len < str_size - 1 && at_index < str_size - 1)
{
@ -673,7 +673,7 @@ static int do_server_list(float x, float y, int *scroll_index, int *selected_ind
}
*scroll_index = do_scroll_bar_vert(scroll_index, x + real_width - 16, y, real_height,
min(num_servers - visible_items, 0), *scroll_index);
max(num_servers - visible_items, 0), *scroll_index);
return r;
}
@ -837,8 +837,11 @@ static int settings_controls_render()
static const int MAX_RESOLUTIONS = 128;
static int settings_video_render_select_mode()
{
video_mode modes[MAX_RESOLUTIONS];
int num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS);
static video_mode modes[MAX_RESOLUTIONS];
static int num_modes = -1;
if(num_modes == -1)
num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS);
static int scroll_index = 0;
scroll_index = do_scroll_bar_vert(&scroll_index, 500, row1_y, 40 * 7, num_modes - 7, scroll_index);
@ -1033,10 +1036,10 @@ static int settings_render(bool ingame)
extern int gametype;
static int ingame_main_render()
{
static int menu_resume, menu_active, menu_quit, menu_settings;
char buf[128];
static int menu_resume, menu_quit, menu_settings;
/*if (gametype == GAMETYPE_TDM)
{
char buf[128];
// Switch team
ui_do_label(100,100,"Switch Team",40);
sprintf(buf,"Team: %s",local_player->team ? "A" : "B");
@ -1255,7 +1258,7 @@ static int kerning_render()
}
static int render_popup(const char *caption, const char *text, const char *button_text)
int render_popup(const char *caption, const char *text, const char *button_text)
{
float tw;
@ -1278,9 +1281,14 @@ static int render_popup(const char *caption, const char *text, const char *butto
tw = gfx_pretty_text_width(32.0f, text);
ui_do_label(x+w/2-tw/2, y+130, text, 32.0f);
if(button_text)
{
static int back_button = 0;
if(ui_do_button(&back_button, button_text, 0, x+w/2-100, y+220, 200, 48, draw_teewars_button))
return 1;
if(inp_key_down(input::esc) || inp_key_down(input::enter))
return 1;
}
return 0;
}

View file

@ -175,6 +175,11 @@ event_handler::event_handler()
void *event_handler::create(int type, int size, int target)
{
if(num_events == MAX_EVENTS)
return 0;
if(current_offset+size >= MAX_DATASIZE)
return 0;
void *p = &data[current_offset];
offsets[num_events] = current_offset;
types[num_events] = type;
@ -215,8 +220,7 @@ entity::entity(int objtype)
flags = FLAG_ALIVE;
proximity_radius = 0;
current_id++;
id = current_id;
id = snap_new_id();
next_entity = 0;
prev_entity = 0;
@ -226,10 +230,9 @@ entity::entity(int objtype)
entity::~entity()
{
snap_free_id(id);
}
int entity::current_id = 1;
//////////////////////////////////////////////////
// game world
//////////////////////////////////////////////////
@ -538,7 +541,7 @@ void gameobject::tick()
void gameobject::snap(int snapping_client)
{
obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, id, sizeof(obj_game));
obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, 0, sizeof(obj_game));
game->paused = world.paused;
game->game_over = game_over_tick==-1?0:1;
game->sudden_death = sudden_death;
@ -563,7 +566,7 @@ int gameobject::getteam(int notthisid)
return numplayers[0] > numplayers[1] ? 1 : 0;
}
gameobject gameobj;
gameobject *gameobj = 0;
//////////////////////////////////////////////////
// projectile
@ -733,6 +736,15 @@ void player::init()
team = 0;
extrapowerflags = 0;
ninjaactivationtick = 0;
latency_accum = 0;
latency_accum_min = 0;
latency_accum_max = 0;
latency_avg = 0;
latency_min = 0;
latency_max = 0;
reset();
}
@ -750,11 +762,26 @@ void player::reset()
die_tick = 0;
damage_taken = 0;
state = STATE_UNKNOWN;
mem_zero(&input, sizeof(input));
mem_zero(&previnput, sizeof(previnput));
last_action = -1;
emote_stop = 0;
damage_taken_tick = 0;
attack_tick = 0;
}
void player::destroy() { }
void player::set_weapon(int w)
{
last_weapon = active_weapon;
active_weapon = w;
}
void player::respawn()
{
spawning = true;
@ -788,7 +815,7 @@ void player::try_respawn()
defered_pos = pos;
health = data->playerinfo[gameobj.gametype].maxhealth;
health = data->playerinfo[gameobj->gametype].maxhealth;
armor = 0;
jumped = 0;
dead = false;
@ -803,9 +830,11 @@ void player::try_respawn()
weapons[WEAPON_HAMMER].got = true;
weapons[WEAPON_HAMMER].ammo = -1;
weapons[WEAPON_GUN].got = true;
weapons[WEAPON_GUN].ammo = data->weapons[active_weapon].maxammo;
weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo;
active_weapon = WEAPON_GUN;
last_weapon = WEAPON_HAMMER;
reload_timer = 0;
// Create sound and spawn effects
@ -849,7 +878,7 @@ int player::handle_ninja()
if ((server_tick() - ninjaactivationtick) > (data->weapons[WEAPON_NINJA].duration * server_tickspeed() / 1000))
{
// time's up, return
active_weapon = WEAPON_GUN;
active_weapon = last_weapon;
return 0;
}
@ -935,6 +964,18 @@ int player::handle_ninja()
int player::handle_weapons()
{
if(config.stress)
{
for(int i = 0; i < NUM_WEAPONS; i++)
{
weapons[i].got = true;
weapons[i].ammo = 10;
}
if(reload_timer) // twice as fast reload
reload_timer--;
}
// check reload timer
if(reload_timer)
{
@ -954,8 +995,8 @@ int player::handle_weapons()
if (active_weapon != input.activeweapon)
create_sound(pos, SOUND_WEAPON_SWITCH);
last_weapon = active_weapon;
active_weapon = input.activeweapon;
}
if(!previnput.fire && input.fire)
@ -1342,6 +1383,8 @@ void player::tick_defered()
void player::die(int killer, int weapon)
{
dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d", killer, players[killer].name, client_id, name, weapon);
// send the kill message
msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL);
msg_pack_int(killer);
@ -1372,7 +1415,7 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon)
if(from == client_id)
dmg = max(1, dmg/2);
if (gameobj.gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team)
if (gameobj->gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team)
return false;
damage_taken++;
@ -1505,7 +1548,12 @@ void player::snap(int snaping_client)
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);
float a = 0;
if(input.target_x == 0)
a = atan((float)input.target_y);
else
a = atan((float)input.target_y/(float)input.target_x);
if(input.target_x < 0)
a = a+pi;
@ -1517,7 +1565,7 @@ void player::snap(int snaping_client)
player->state = state;
}
player players[MAX_CLIENTS];
player *players;
//////////////////////////////////////////////////
// powerup
@ -1569,18 +1617,18 @@ void powerup::tick()
switch (type)
{
case POWERUP_HEALTH:
if(pplayer->health < data->playerinfo[gameobj.gametype].maxhealth)
if(pplayer->health < data->playerinfo[gameobj->gametype].maxhealth)
{
create_sound(pos, SOUND_PICKUP_HEALTH, 0);
pplayer->health = min((int)data->playerinfo[gameobj.gametype].maxhealth, pplayer->health + data->powerupinfo[type].amount);
pplayer->health = min((int)data->playerinfo[gameobj->gametype].maxhealth, pplayer->health + data->powerupinfo[type].amount);
respawntime = data->powerupinfo[type].respawntime;
}
break;
case POWERUP_ARMOR:
if(pplayer->armor < data->playerinfo[gameobj.gametype].maxarmor)
if(pplayer->armor < data->playerinfo[gameobj->gametype].maxarmor)
{
create_sound(pos, SOUND_PICKUP_ARMOR, 0);
pplayer->armor = min((int)data->playerinfo[gameobj.gametype].maxarmor, pplayer->armor + data->powerupinfo[type].amount);
pplayer->armor = min((int)data->playerinfo[gameobj->gametype].maxarmor, pplayer->armor + data->powerupinfo[type].amount);
respawntime = data->powerupinfo[type].respawntime;
}
break;
@ -1607,6 +1655,7 @@ void powerup::tick()
// activate ninja on target player
pplayer->ninjaactivationtick = server_tick();
pplayer->weapons[WEAPON_NINJA].got = true;
pplayer->last_weapon = pplayer->active_weapon;
pplayer->active_weapon = WEAPON_NINJA;
respawntime = data->powerupinfo[type].respawntime;
create_sound(pos, SOUND_PICKUP_NINJA);
@ -1635,8 +1684,11 @@ void powerup::tick()
};
if(respawntime >= 0)
{
dbg_msg("game", "pickup player='%d:%s' item=%d/%d", pplayer->client_id, pplayer->name, type, subtype);
spawntick = server_tick() + server_tickspeed() * respawntime;
}
}
}
void powerup::snap(int snapping_client)
@ -1757,18 +1809,24 @@ void create_damageind(vec2 p, float angle, int amount)
{
float f = mix(s, e, float(i+1)/float(amount+2));
ev_damageind *ev = (ev_damageind *)events.create(EVENT_DAMAGEINDICATION, sizeof(ev_damageind));
if(ev)
{
ev->x = (int)p.x;
ev->y = (int)p.y;
ev->angle = (int)(f*256.0f);
}
}
}
void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
{
// create the event
ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion));
if(ev)
{
ev->x = (int)p.x;
ev->y = (int)p.y;
}
if (!bnodamage)
{
@ -1796,24 +1854,33 @@ void create_smoke(vec2 p)
{
// create the event
ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion));
if(ev)
{
ev->x = (int)p.x;
ev->y = (int)p.y;
}
}
void create_spawn(vec2 p)
{
// create the event
ev_spawn *ev = (ev_spawn *)events.create(EVENT_SPAWN, sizeof(ev_spawn));
if(ev)
{
ev->x = (int)p.x;
ev->y = (int)p.y;
}
}
void create_death(vec2 p)
{
// create the event
ev_death *ev = (ev_death *)events.create(EVENT_DEATH, sizeof(ev_death));
if(ev)
{
ev->x = (int)p.x;
ev->y = (int)p.y;
}
}
void create_targetted_sound(vec2 pos, int sound, int target, int loopingflags)
@ -1823,9 +1890,12 @@ void create_targetted_sound(vec2 pos, int sound, int target, int loopingflags)
// create a sound
ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND, sizeof(ev_sound), target);
if(ev)
{
ev->x = (int)pos.x;
ev->y = (int)pos.y;
ev->sound = sound | loopingflags;
}
}
void create_sound(vec2 pos, int sound, int loopingflags)
@ -1864,7 +1934,7 @@ void mods_tick()
world.tick();
if(world.paused) // make sure that the game object always updates
gameobj.tick();
gameobj->tick();
if(debug_bots)
{
@ -1905,6 +1975,11 @@ void mods_client_input(int client_id, void *input)
void send_chat_all(int cid, const char *msg)
{
if(cid >= 0 && cid < MAX_CLIENTS)
dbg_msg("chat", "%d:%s: %s", cid, players[cid].name, msg);
else
dbg_msg("chat", "*** %s", msg);
msg_pack_start(MSG_CHAT, MSGFLAG_VITAL);
msg_pack_int(cid);
msg_pack_string(msg, 512);
@ -1928,10 +2003,13 @@ void mods_client_enter(int client_id)
else
strcpy(players[client_id].name, "(bot)");
if (gameobj.gametype == GAMETYPE_TDM)
dbg_msg("game", "join player='%d:%s'", client_id, players[client_id].name);
if (gameobj->gametype == GAMETYPE_TDM)
{
// Check which team the player should be on
players[client_id].team = gameobj.getteam(client_id);
players[client_id].team = gameobj->getteam(client_id);
}
msg_pack_start(MSG_SETNAME, MSGFLAG_VITAL);
@ -1963,7 +2041,8 @@ void mods_client_drop(int client_id)
sprintf(buf, "%s has left the game", players[client_id].name);
send_chat_all(-1, buf);
dbg_msg("mods", "client drop %d", client_id);
dbg_msg("game", "leave player='%d:%s'", client_id, players[client_id].name);
world.remove_entity(&players[client_id]);
players[client_id].client_id = -1;
}
@ -1990,6 +2069,9 @@ void mods_init()
data = load_data_from_memory(internal_data);
col_init(32);
players = new player[MAX_CLIENTS];
gameobj = new gameobject;
int start, num;
map_get_type(MAPRES_ITEM, &start, &num);
@ -1999,7 +2081,7 @@ void mods_init()
mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0);
int type = -1;
int subtype = -1;
int subtype = 0;
switch(it->type)
{
@ -2043,7 +2125,7 @@ void mods_init()
}
}
world.insert_entity(&gameobj);
world.insert_entity(gameobj);
}
void mods_shutdown() {}

View file

@ -35,7 +35,6 @@ private:
entity *next_type_entity;
int index;
static int current_id;
protected:
int id;
@ -123,7 +122,7 @@ public:
virtual int getteam(int notthisid);
};
extern gameobject gameobj;
extern gameobject *gameobj;
// TODO: move to seperate file
@ -214,6 +213,7 @@ public:
bool got;
} weapons[NUM_WEAPONS];
int active_weapon;
int last_weapon;
int reload_timer;
int attack_tick;
@ -292,6 +292,8 @@ public:
bool is_grounded();
void set_weapon(int w);
void release_hooked();
void release_hooks();
@ -307,7 +309,7 @@ public:
virtual void snap(int snaping_client);
};
extern player players[MAX_CLIENTS];
extern player *players;
// TODO: move to seperate file
class flag : public entity

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 76 KiB