mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 01:58:19 +00:00
a lot of changes. client side prediction added
This commit is contained in:
parent
350e968f51
commit
3f4587ede8
Binary file not shown.
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 71 KiB |
|
@ -201,7 +201,7 @@ function build(settings)
|
|||
engine_settings = settings:copy()
|
||||
|
||||
if family == "windows" then
|
||||
engine_settings.cc.flags = "/wd4244 /TP"
|
||||
engine_settings.cc.flags = "/wd4244"
|
||||
else
|
||||
engine_settings.cc.flags = "-Wall"
|
||||
engine_settings.linker.flags = ""
|
||||
|
@ -231,12 +231,12 @@ function build(settings)
|
|||
objs = Compile(settings, tools_src)
|
||||
tools = {}
|
||||
for i,v in objs do
|
||||
toolname = PathFilename(file_base(v))
|
||||
toolname = PathFilename(PathBase(v))
|
||||
tools[i] = Link(settings, toolname, v, engine)
|
||||
end
|
||||
|
||||
-- build client, server and master server
|
||||
client_exe = Link(settings, "teewars", game_shared, game_client, engine, client, editor, glfw, pa, client_link_other)
|
||||
client_exe = Link(settings, "teewars", game_shared, game_client, engine, client, editor, glfw, pa, client_libs, client_link_other)
|
||||
server_exe = Link(server_settings, "teewars_srv", engine, server, game_shared, game_server)
|
||||
masterserver_exe = Link(server_settings, "mastersrv", masterserver, engine)
|
||||
|
||||
|
|
|
@ -18,12 +18,26 @@
|
|||
|
||||
#include <mastersrv/mastersrv.h>
|
||||
|
||||
/*
|
||||
Server Time
|
||||
Client Mirror Time
|
||||
Client Predicted Time
|
||||
|
||||
Snapshot Latency
|
||||
Downstream latency
|
||||
|
||||
Prediction Latency
|
||||
Upstream latency
|
||||
*/
|
||||
static int info_request_begin;
|
||||
static int info_request_end;
|
||||
static int snapshot_part;
|
||||
static int64 local_start_time;
|
||||
static int64 game_start_time;
|
||||
static float latency = 0;
|
||||
|
||||
static float snapshot_latency = 0;
|
||||
static float prediction_latency = 0;
|
||||
|
||||
static int extra_polating = 0;
|
||||
static int debug_font;
|
||||
static float frametime = 0.0001f;
|
||||
|
@ -34,9 +48,25 @@ static int window_must_refocus = 0;
|
|||
static int snaploss = 0;
|
||||
static int snapcrcerrors = 0;
|
||||
|
||||
static int current_recv_tick = 0;
|
||||
|
||||
// current time
|
||||
static int current_tick = 0;
|
||||
static float intratick = 0;
|
||||
|
||||
// predicted time
|
||||
static int current_predtick = 0;
|
||||
static float intrapredtick = 0;
|
||||
|
||||
static struct // TODO: handle input better
|
||||
{
|
||||
int data[MAX_INPUT_SIZE]; // the input data
|
||||
int tick; // the tick that the input is for
|
||||
float latency; // prediction latency when we sent this input
|
||||
} inputs[200];
|
||||
static int current_input = 0;
|
||||
|
||||
|
||||
// --- input snapping ---
|
||||
static int input_data[MAX_INPUT_SIZE] = {0};
|
||||
static int input_data_size;
|
||||
|
@ -100,32 +130,13 @@ static void snap_init()
|
|||
}
|
||||
|
||||
// ------ time functions ------
|
||||
float client_intratick()
|
||||
{
|
||||
return intratick;
|
||||
}
|
||||
|
||||
int client_tick()
|
||||
{
|
||||
return current_tick;
|
||||
}
|
||||
|
||||
int client_tickspeed()
|
||||
{
|
||||
return SERVER_TICK_SPEED;
|
||||
}
|
||||
|
||||
float client_frametime()
|
||||
{
|
||||
return frametime;
|
||||
}
|
||||
|
||||
float client_localtime()
|
||||
{
|
||||
return (time_get()-local_start_time)/(float)(time_freq());
|
||||
}
|
||||
|
||||
int menu_loop(); // TODO: what is this?
|
||||
float client_intratick() { return intratick; }
|
||||
float client_intrapredtick() { return intrapredtick; }
|
||||
int client_tick() { return current_tick; }
|
||||
int client_predtick() { return current_predtick; }
|
||||
int client_tickspeed() { return SERVER_TICK_SPEED; }
|
||||
float client_frametime() { return frametime; }
|
||||
float client_localtime() { return (time_get()-local_start_time)/(float)(time_freq()); }
|
||||
|
||||
// ----- send functions -----
|
||||
int client_send_msg()
|
||||
|
@ -176,19 +187,46 @@ static void client_send_error(const char *error)
|
|||
//send_packet(&p);
|
||||
//send_packet(&p);
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
static void client_send_input()
|
||||
{
|
||||
msg_pack_start_system(NETMSG_INPUT, 0);
|
||||
msg_pack_int(current_predtick);
|
||||
msg_pack_int(input_data_size);
|
||||
|
||||
inputs[current_input].tick = current_predtick;
|
||||
inputs[current_input].latency = prediction_latency;
|
||||
|
||||
int i;
|
||||
for(i = 0; i < input_data_size/4; i++)
|
||||
{
|
||||
inputs[current_input].data[i] = input_data[i];
|
||||
msg_pack_int(input_data[i]);
|
||||
}
|
||||
|
||||
current_input++;
|
||||
current_input%=200;
|
||||
|
||||
msg_pack_end();
|
||||
client_send_msg();
|
||||
}
|
||||
|
||||
/* TODO: OPT: do this alot smarter! */
|
||||
int *client_get_input(int tick)
|
||||
{
|
||||
int i;
|
||||
int best = -1;
|
||||
for(i = 0; i < 200; i++)
|
||||
{
|
||||
if(inputs[i].tick <= tick && (best == -1 || inputs[best].tick < inputs[i].tick))
|
||||
best = i;
|
||||
}
|
||||
|
||||
if(best != -1)
|
||||
return (int *)inputs[best].data;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------ server browse ----
|
||||
static struct
|
||||
|
@ -337,7 +375,17 @@ void client_connect(const char *server_address_str)
|
|||
|
||||
netclient_connect(net, &server_address);
|
||||
client_set_state(CLIENTSTATE_CONNECTING);
|
||||
current_tick = 0;
|
||||
|
||||
current_recv_tick = 0;
|
||||
|
||||
// reset input
|
||||
int i;
|
||||
for(i = 0; i < 200; i++)
|
||||
inputs[i].tick = -1;
|
||||
current_input = 0;
|
||||
|
||||
snapshot_latency = 0;
|
||||
prediction_latency = 0;
|
||||
}
|
||||
|
||||
void client_disconnect()
|
||||
|
@ -375,11 +423,12 @@ 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 snaploss: %d latency: %4.0f %c mem %dk gfxmem: %dk fps: %3d",
|
||||
sprintf(buffer, "send: %6d recv: %6d snaploss: %d snaplatency: %4.2f %c predlatency: %4.2f mem %dk gfxmem: %dk fps: %3d",
|
||||
(current.send_bytes-prev.send_bytes)*10,
|
||||
(current.recv_bytes-prev.recv_bytes)*10,
|
||||
snaploss,
|
||||
latency*1000.0f, extra_polating?'E':' ',
|
||||
snapshot_latency*1000.0f, extra_polating?'E':' ',
|
||||
prediction_latency*1000.0f,
|
||||
mem_allocated()/1024,
|
||||
gfx_memory_usage()/1024,
|
||||
(int)(1.0f/frametime_avg));
|
||||
|
@ -526,10 +575,12 @@ static void client_process_packet(NETPACKET *packet)
|
|||
//dbg_msg("client/network", "got snapshot");
|
||||
int game_tick = msg_unpack_int();
|
||||
int delta_tick = game_tick-msg_unpack_int();
|
||||
int input_predtick = msg_unpack_int();
|
||||
int time_left = msg_unpack_int();
|
||||
int num_parts = 1;
|
||||
int part = 0;
|
||||
int part_size = 0;
|
||||
int crc;
|
||||
int crc = 0;
|
||||
|
||||
if(msg != NETMSG_SNAPEMPTY)
|
||||
{
|
||||
|
@ -537,7 +588,25 @@ static void client_process_packet(NETPACKET *packet)
|
|||
part_size = msg_unpack_int();
|
||||
}
|
||||
|
||||
if(snapshot_part == part && game_tick > current_tick)
|
||||
/* TODO: adjust our prediction time */
|
||||
if(time_left)
|
||||
{
|
||||
int k;
|
||||
for(k = 0; k < 200; k++) /* TODO: do this better */
|
||||
{
|
||||
if(inputs[k].tick == input_predtick)
|
||||
{
|
||||
float wanted_latency = inputs[k].latency - time_left/1000.0f + 0.01f;
|
||||
prediction_latency = prediction_latency*0.95f + wanted_latency*0.05f;
|
||||
//dbg_msg("DEBUG", "predlatency=%f", prediction_latency);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//dbg_msg("DEBUG", "new predlatency=%f", prediction_latency);
|
||||
|
||||
if(snapshot_part == part && game_tick > current_recv_tick)
|
||||
{
|
||||
// TODO: clean this up abit
|
||||
const char *d = (const char *)msg_unpack_raw(part_size);
|
||||
|
@ -568,6 +637,7 @@ static void client_process_packet(NETPACKET *packet)
|
|||
dbg_msg("client", "error, couldn't find the delta snapshot");
|
||||
|
||||
// ack snapshot
|
||||
// TODO: combine this with the input message
|
||||
msg_pack_start_system(NETMSG_SNAPACK, 0);
|
||||
msg_pack_int(-1);
|
||||
msg_pack_end();
|
||||
|
@ -637,9 +707,9 @@ static void client_process_packet(NETPACKET *packet)
|
|||
recived_snapshots++;
|
||||
|
||||
|
||||
if(current_tick > 0)
|
||||
snaploss += game_tick-current_tick-1;
|
||||
current_tick = game_tick;
|
||||
if(current_recv_tick > 0)
|
||||
snaploss += game_tick-current_recv_tick-1;
|
||||
current_recv_tick = game_tick;
|
||||
|
||||
// we got two snapshots until we see us self as connected
|
||||
if(recived_snapshots == 2)
|
||||
|
@ -661,7 +731,7 @@ static void client_process_packet(NETPACKET *packet)
|
|||
|
||||
int64 wanted = game_start_time+(game_tick*time_freq())/50;
|
||||
float current_latency = (now-wanted)/(float)time_freq();
|
||||
latency = latency*0.95f+current_latency*0.05f;
|
||||
snapshot_latency = snapshot_latency*0.95f+current_latency*0.05f;
|
||||
|
||||
// ack snapshot
|
||||
msg_pack_start_system(NETMSG_SNAPACK, 0);
|
||||
|
@ -693,7 +763,6 @@ static void client_pump_network()
|
|||
// check for errors
|
||||
if(client_state() != CLIENTSTATE_OFFLINE && netclient_state(net) == NETSTATE_OFFLINE)
|
||||
{
|
||||
// TODO: add message to the user there
|
||||
client_set_state(CLIENTSTATE_OFFLINE);
|
||||
dbg_msg("client", "offline error='%s'", netclient_error_string(net));
|
||||
}
|
||||
|
@ -754,9 +823,6 @@ static void client_run(const char *direct_connect_server)
|
|||
if(direct_connect_server)
|
||||
client_connect(direct_connect_server);
|
||||
|
||||
int64 game_starttime = time_get();
|
||||
int64 last_input = game_starttime;
|
||||
|
||||
int64 reporttime = time_get();
|
||||
int64 reportinterval = time_freq()*1;
|
||||
int frames = 0;
|
||||
|
@ -771,14 +837,15 @@ static void client_run(const char *direct_connect_server)
|
|||
// switch snapshot
|
||||
if(recived_snapshots >= 3)
|
||||
{
|
||||
int repredict = 0;
|
||||
int64 now = time_get();
|
||||
while(1)
|
||||
{
|
||||
SNAPSTORAGE_HOLDER *cur = snapshots[SNAP_CURRENT];
|
||||
int64 tickstart = game_start_time + (cur->tick+1)*time_freq()/50;
|
||||
int64 t = tickstart;
|
||||
if(latency > 0)
|
||||
t += (int64)(time_freq()*(latency*1.1f));
|
||||
if(snapshot_latency > 0)
|
||||
t += (int64)(time_freq()*(snapshot_latency*1.1f));
|
||||
|
||||
if(t < now)
|
||||
{
|
||||
|
@ -787,8 +854,15 @@ static void client_run(const char *direct_connect_server)
|
|||
{
|
||||
snapshots[SNAP_PREV] = snapshots[SNAP_CURRENT];
|
||||
snapshots[SNAP_CURRENT] = next;
|
||||
|
||||
// set tick
|
||||
current_tick = snapshots[SNAP_CURRENT]->tick;
|
||||
|
||||
if(snapshots[SNAP_CURRENT] && snapshots[SNAP_PREV])
|
||||
{
|
||||
modc_newsnapshot();
|
||||
repredict = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -806,31 +880,33 @@ static void client_run(const char *direct_connect_server)
|
|||
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));
|
||||
if(snapshot_latency > 0)
|
||||
curtick_start += (int64)(time_freq()*(snapshot_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));
|
||||
if(snapshot_latency > 0)
|
||||
prevtick_start += (int64)(time_freq()*(snapshot_latency*1.1f));
|
||||
|
||||
intratick = (now - prevtick_start) / (float)(curtick_start-prevtick_start);
|
||||
|
||||
// 25 frames ahead
|
||||
int new_predtick = current_tick+prediction_latency*SERVER_TICK_SPEED;
|
||||
if(new_predtick > current_predtick)
|
||||
{
|
||||
//dbg_msg("")
|
||||
current_predtick = new_predtick;
|
||||
repredict = 1;
|
||||
|
||||
// send input
|
||||
client_send_input();
|
||||
}
|
||||
}
|
||||
|
||||
if(repredict)
|
||||
modc_predict();
|
||||
}
|
||||
|
||||
// send input
|
||||
if(client_state() == CLIENTSTATE_ONLINE)
|
||||
{
|
||||
if(config.stress&1 && client_localtime() > 10.0f)
|
||||
client_disconnect();
|
||||
|
||||
if(input_is_changed || time_get() > last_input+time_freq())
|
||||
{
|
||||
client_send_input();
|
||||
input_is_changed = 0;
|
||||
last_input = time_get();
|
||||
}
|
||||
}
|
||||
|
||||
// STRESS TEST: join the server again
|
||||
if(client_state() == CLIENTSTATE_OFFLINE && config.stress && (frames%100) == 0)
|
||||
client_connect(config.cl_stress_server);
|
||||
|
||||
|
@ -838,8 +914,6 @@ static void client_run(const char *direct_connect_server)
|
|||
inp_update();
|
||||
|
||||
// refocus
|
||||
// TODO: fixme
|
||||
|
||||
if(!gfx_window_active())
|
||||
{
|
||||
if(window_must_refocus == 0)
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
#include "system.h"
|
||||
#include <string.h>
|
||||
|
||||
// Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign
|
||||
/* Format: ESDDDDDD EDDDDDDD EDD... Extended, Data, Sign */
|
||||
unsigned char *vint_pack(unsigned char *dst, int i)
|
||||
{
|
||||
*dst = (i>>25)&0x40; // set sign bit if i<0
|
||||
i = i^(i>>31); // if(i<0) i = ~i
|
||||
*dst = (i>>25)&0x40; /* set sign bit if i<0 */
|
||||
i = i^(i>>31); /* if(i<0) i = ~i */
|
||||
|
||||
*dst |= i&0x3F; // pack 6bit into dst
|
||||
i >>= 6; // discard 6 bits
|
||||
*dst |= i&0x3F; /* pack 6bit into dst */
|
||||
i >>= 6; /* discard 6 bits */
|
||||
if(i)
|
||||
{
|
||||
*dst |= 0x80; // set extend bit
|
||||
*dst |= 0x80; /* set extend bit */
|
||||
while(1)
|
||||
{
|
||||
dst++;
|
||||
*dst = i&(0x7F); // pack 7bit
|
||||
i >>= 7; // discard 7 bits
|
||||
*dst |= (i!=0)<<7; // set extend bit (may branch)
|
||||
*dst = i&(0x7F); /* pack 7bit */
|
||||
i >>= 7; /* discard 7 bits */
|
||||
*dst |= (i!=0)<<7; /* set extend bit (may branch) */
|
||||
if(!i)
|
||||
break;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ const unsigned char *vint_unpack(const unsigned char *src, int *i)
|
|||
} while(0);
|
||||
|
||||
src++;
|
||||
*i ^= -sign; // if(sign) *i = ~(*i)
|
||||
*i ^= -sign; /* if(sign) *i = ~(*i) */
|
||||
return src;
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ long intpack_compress(const void *src_, int size, void *dst_)
|
|||
}
|
||||
|
||||
|
||||
//
|
||||
/* */
|
||||
long zerobit_compress(const void *src_, int size, void *dst_)
|
||||
{
|
||||
unsigned char *src = (unsigned char *)src_;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
#include "config.h"
|
||||
|
||||
|
||||
// buffered stream for reading lines, should perhaps be something smaller
|
||||
/* buffered stream for reading lines, should perhaps be something smaller */
|
||||
typedef struct
|
||||
{
|
||||
char buffer[4*1024];
|
||||
|
@ -33,9 +33,9 @@ char *linereader_get(LINEREADER *lr)
|
|||
{
|
||||
if(lr->buffer_pos >= lr->buffer_size)
|
||||
{
|
||||
// fetch more
|
||||
/* fetch more */
|
||||
|
||||
// move the remaining part to the front
|
||||
/* move the remaining part to the front */
|
||||
unsigned left = lr->buffer_size - line_start;
|
||||
if(line_start > lr->buffer_size)
|
||||
left = 0;
|
||||
|
@ -43,7 +43,7 @@ char *linereader_get(LINEREADER *lr)
|
|||
mem_move(lr->buffer, &lr->buffer[line_start], left);
|
||||
lr->buffer_pos = left;
|
||||
|
||||
// fill the buffer
|
||||
/* fill the buffer */
|
||||
unsigned read = io_read(lr->io, &lr->buffer[lr->buffer_pos], lr->buffer_max_size-lr->buffer_pos);
|
||||
lr->buffer_size = left + read;
|
||||
line_start = 0;
|
||||
|
@ -52,20 +52,20 @@ char *linereader_get(LINEREADER *lr)
|
|||
{
|
||||
if(left)
|
||||
{
|
||||
lr->buffer[left] = 0; // return the last line
|
||||
lr->buffer[left] = 0; /* return the last line */
|
||||
lr->buffer_pos = left;
|
||||
lr->buffer_size = left;
|
||||
return lr->buffer;
|
||||
}
|
||||
else
|
||||
return 0x0; // we are done!
|
||||
return 0x0; /* we are done! */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(lr->buffer[lr->buffer_pos] == '\n' || lr->buffer[lr->buffer_pos] == '\r')
|
||||
{
|
||||
// line found
|
||||
/* line found */
|
||||
lr->buffer[lr->buffer_pos] = 0;
|
||||
lr->buffer_pos++;
|
||||
return &lr->buffer[line_start];
|
||||
|
@ -176,7 +176,6 @@ void config_save(const char *filename)
|
|||
|
||||
dbg_msg("config/save", "saving config to %s", filename);
|
||||
|
||||
//file_stream file;
|
||||
IOHANDLE file = io_open(filename, IOFLAG_WRITE);
|
||||
|
||||
if(file)
|
||||
|
|
|
@ -100,9 +100,6 @@ DATAFILE *datafile_load(const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
//if(DEBUG)
|
||||
//dbg_msg("datafile", "loading. size=%d", datafile.size);
|
||||
|
||||
/* read in the rest except the data */
|
||||
int size = 0;
|
||||
size += header.num_item_types*sizeof(DATAFILE_ITEM_TYPE);
|
||||
|
@ -112,8 +109,8 @@ DATAFILE *datafile_load(const char *filename)
|
|||
size += header.item_size;
|
||||
|
||||
int allocsize = size;
|
||||
allocsize += sizeof(DATAFILE); // add space for info structure
|
||||
allocsize += header.num_raw_data*sizeof(void*); // add space for data pointers
|
||||
allocsize += sizeof(DATAFILE); /* add space for info structure */
|
||||
allocsize += header.num_raw_data*sizeof(void*); /* add space for data pointers */
|
||||
|
||||
df = (DATAFILE*)mem_alloc(allocsize, 1);
|
||||
df->header = header;
|
||||
|
@ -207,9 +204,6 @@ int datafile_num_data(DATAFILE *df)
|
|||
/* always returns the size in the file */
|
||||
int datafile_get_datasize(DATAFILE *df, int index)
|
||||
{
|
||||
//if(df->header.version == 4)
|
||||
// return df->info.data_sizes[index];
|
||||
|
||||
if(index == df->header.num_raw_data-1)
|
||||
return df->header.data_size-df->info.data_offsets[index];
|
||||
return df->info.data_offsets[index+1]-df->info.data_offsets[index];
|
||||
|
@ -217,7 +211,7 @@ int datafile_get_datasize(DATAFILE *df, int index)
|
|||
|
||||
void *datafile_get_data(DATAFILE *df, int index)
|
||||
{
|
||||
// load it if needed
|
||||
/* load it if needed */
|
||||
if(!df->data_ptrs[index])
|
||||
{
|
||||
/* fetch the data size */
|
||||
|
|
|
@ -5,7 +5,7 @@ typedef struct DATAFILE_t DATAFILE;
|
|||
/* read access */
|
||||
DATAFILE *datafile_load(const char *filename);
|
||||
DATAFILE *datafile_load_old(const char *filename);
|
||||
void *datafile_get_data(DATAFILE *df, int index); // automaticly load the data for the item
|
||||
void *datafile_get_data(DATAFILE *df, int index);
|
||||
int datafile_get_datasize(DATAFILE *df, int index);
|
||||
void datafile_unload_data(DATAFILE *df, int index);
|
||||
void *datafile_get_item(DATAFILE *df, int index, int *type, int *id);
|
||||
|
|
|
@ -706,15 +706,13 @@ int modmenu_render(int ingame);
|
|||
|
||||
/* undocumented callbacks */
|
||||
void modc_message(int msg);
|
||||
void modc_predict();
|
||||
void mods_message(int msg, int client_id);
|
||||
|
||||
|
||||
const char *modc_net_version();
|
||||
const char *mods_net_version();
|
||||
|
||||
// unused
|
||||
// const char *modc_version();
|
||||
// const char *mods_version();
|
||||
|
||||
/* server */
|
||||
int server_getclientinfo(int client_id, CLIENT_INFO *info);
|
||||
int server_tick();
|
||||
|
@ -769,14 +767,16 @@ int client_send_msg();
|
|||
|
||||
/* client */
|
||||
int client_tick();
|
||||
int client_predtick();
|
||||
float client_intratick();
|
||||
float client_intrapredtick();
|
||||
int client_tickspeed();
|
||||
float client_frametime();
|
||||
float client_localtime();
|
||||
|
||||
int client_state();
|
||||
const char *client_error_string();
|
||||
|
||||
int *client_get_input(int tick);
|
||||
|
||||
void client_connect(const char *address);
|
||||
void client_disconnect();
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
|
||||
static SNAPBUILD builder;
|
||||
|
||||
static int64 lasttick;
|
||||
static int64 game_start_time;
|
||||
static int current_tick = 0;
|
||||
|
||||
static int64 lastheartbeat;
|
||||
static NETADDR4 master_server;
|
||||
|
||||
|
@ -36,11 +38,11 @@ void *snap_new_item(int type, int id, int size)
|
|||
typedef struct
|
||||
{
|
||||
short next;
|
||||
short state; // 0 = free, 1 = alloced, 2 = timed
|
||||
short state; /* 0 = free, 1 = alloced, 2 = timed */
|
||||
int timeout_tick;
|
||||
} SNAP_ID;
|
||||
|
||||
static const int MAX_IDS = 8*1024; // should be lowered
|
||||
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;
|
||||
|
@ -49,6 +51,42 @@ static int snap_id_usage;
|
|||
static int snap_id_inusage;
|
||||
static int snap_id_inited = 0;
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
SRVCLIENT_STATE_EMPTY = 0,
|
||||
SRVCLIENT_STATE_CONNECTING = 1,
|
||||
SRVCLIENT_STATE_INGAME = 2,
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int data[MAX_INPUT_SIZE];
|
||||
int pred_tick; /* tick that the client predicted for the input */
|
||||
int game_tick; /* the tick that was chosen for the input */
|
||||
int64 timeleft; /* how much time in ms there were left before this should be applied */
|
||||
} CLIENT_INPUT;
|
||||
|
||||
/* */
|
||||
typedef struct
|
||||
{
|
||||
/* connection state info */
|
||||
int state;
|
||||
int latency;
|
||||
|
||||
int last_acked_snapshot;
|
||||
SNAPSTORAGE snapshots;
|
||||
|
||||
CLIENT_INPUT inputs[200]; /* TODO: handle input better */
|
||||
int current_input;
|
||||
|
||||
char name[MAX_NAME_LENGTH];
|
||||
char clan[MAX_CLANNAME_LENGTH];
|
||||
} CLIENT;
|
||||
|
||||
static CLIENT clients[MAX_CLIENTS];
|
||||
static NETSERVER *net;
|
||||
|
||||
static void snap_init_id()
|
||||
{
|
||||
int i;
|
||||
|
@ -72,17 +110,17 @@ int snap_new_id()
|
|||
{
|
||||
dbg_assert(snap_id_inited == 1, "requesting id too soon");
|
||||
|
||||
// process timed ids
|
||||
/* 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
|
||||
/* 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
|
||||
/* remove it from the timed list */
|
||||
snap_first_timed_id = next_timed;
|
||||
if(snap_first_timed_id == -1)
|
||||
snap_last_timed_id = -1;
|
||||
|
@ -120,36 +158,16 @@ void snap_free_id(int id)
|
|||
}
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
SRVCLIENT_STATE_EMPTY = 0,
|
||||
SRVCLIENT_STATE_CONNECTING = 1,
|
||||
SRVCLIENT_STATE_INGAME = 2,
|
||||
};
|
||||
|
||||
//
|
||||
typedef struct
|
||||
{
|
||||
// connection state info
|
||||
int state;
|
||||
int latency;
|
||||
|
||||
int last_acked_snapshot;
|
||||
SNAPSTORAGE snapshots;
|
||||
|
||||
char name[MAX_NAME_LENGTH];
|
||||
char clan[MAX_CLANNAME_LENGTH];
|
||||
} CLIENT;
|
||||
|
||||
static CLIENT clients[MAX_CLIENTS];
|
||||
static int current_tick = 0;
|
||||
static NETSERVER *net;
|
||||
|
||||
int server_tick()
|
||||
{
|
||||
return current_tick;
|
||||
}
|
||||
|
||||
int64 server_tick_start_time(int tick)
|
||||
{
|
||||
return game_start_time + (time_freq()*tick)/SERVER_TICK_SPEED;
|
||||
}
|
||||
|
||||
int server_tickspeed()
|
||||
{
|
||||
return SERVER_TICK_SPEED;
|
||||
|
@ -201,7 +219,7 @@ int server_send_msg(int client_id)
|
|||
|
||||
if(client_id == -1)
|
||||
{
|
||||
// broadcast
|
||||
/* broadcast */
|
||||
int i;
|
||||
for(i = 0; i < MAX_CLIENTS; i++)
|
||||
if(clients[i].state == SRVCLIENT_STATE_INGAME)
|
||||
|
@ -224,10 +242,10 @@ static void server_do_tick()
|
|||
|
||||
static void server_do_snap()
|
||||
{
|
||||
int i, k;
|
||||
mods_presnap();
|
||||
|
||||
int i;
|
||||
for( i = 0; i < MAX_CLIENTS; i++)
|
||||
for(i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(clients[i].state == SRVCLIENT_STATE_INGAME)
|
||||
{
|
||||
|
@ -237,18 +255,18 @@ static void server_do_snap()
|
|||
snapbuild_init(&builder);
|
||||
mods_snap(i);
|
||||
|
||||
// finish snapshot
|
||||
/* finish snapshot */
|
||||
int snapshot_size = snapbuild_finish(&builder, data);
|
||||
int crc = snapshot_crc((SNAPSHOT*)data);
|
||||
|
||||
// remove old snapshos
|
||||
// keep 1 seconds worth of snapshots
|
||||
/* remove old snapshos */
|
||||
/* keep 1 seconds worth of snapshots */
|
||||
snapstorage_purge_until(&clients[i].snapshots, current_tick-SERVER_TICK_SPEED);
|
||||
|
||||
// save it the snapshot
|
||||
/* save it the snapshot */
|
||||
snapstorage_add(&clients[i].snapshots, current_tick, time_get(), snapshot_size, data);
|
||||
|
||||
// find snapshot that we can preform delta against
|
||||
/* find snapshot that we can preform delta against */
|
||||
static SNAPSHOT emptysnap;
|
||||
emptysnap.data_size = 0;
|
||||
emptysnap.num_items = 0;
|
||||
|
@ -262,12 +280,24 @@ static void server_do_snap()
|
|||
delta_tick = clients[i].last_acked_snapshot;
|
||||
}
|
||||
|
||||
// create delta
|
||||
int input_predtick = -1;
|
||||
int64 timeleft = 0;
|
||||
for(k = 0; k < 200; k++) // TODO: do this better
|
||||
{
|
||||
if(clients[i].inputs[k].game_tick == current_tick)
|
||||
{
|
||||
timeleft = clients[i].inputs[k].timeleft;
|
||||
input_predtick = clients[i].inputs[k].pred_tick;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* create delta */
|
||||
int deltasize = snapshot_create_delta(deltashot, (SNAPSHOT*)data, deltadata);
|
||||
|
||||
if(deltasize)
|
||||
{
|
||||
// compress it
|
||||
/* compress it */
|
||||
unsigned char intdata[MAX_SNAPSHOT_SIZE];
|
||||
int intsize = intpack_compress(deltadata, deltasize, intdata);
|
||||
|
||||
|
@ -288,7 +318,9 @@ static void server_do_snap()
|
|||
|
||||
msg_pack_start_system(NETMSG_SNAP, 0);
|
||||
msg_pack_int(current_tick);
|
||||
msg_pack_int(current_tick-delta_tick); // compressed with
|
||||
msg_pack_int(current_tick-delta_tick); /* compressed with */
|
||||
msg_pack_int(input_predtick);
|
||||
msg_pack_int((timeleft*1000)/time_freq());
|
||||
msg_pack_int(crc);
|
||||
msg_pack_int(chunk);
|
||||
msg_pack_raw(&compdata[n*max_size], chunk);
|
||||
|
@ -300,7 +332,9 @@ static void server_do_snap()
|
|||
{
|
||||
msg_pack_start_system(NETMSG_SNAPEMPTY, 0);
|
||||
msg_pack_int(current_tick);
|
||||
msg_pack_int(current_tick-delta_tick); // compressed with
|
||||
msg_pack_int(current_tick-delta_tick); /* compressed with */
|
||||
msg_pack_int(input_predtick);
|
||||
msg_pack_int((timeleft*1000)/time_freq());
|
||||
msg_pack_end();
|
||||
server_send_msg(i);
|
||||
}
|
||||
|
@ -313,9 +347,19 @@ static void server_do_snap()
|
|||
|
||||
static int new_client_callback(int cid, void *user)
|
||||
{
|
||||
int i;
|
||||
clients[cid].state = SRVCLIENT_STATE_CONNECTING;
|
||||
clients[cid].name[0] = 0;
|
||||
clients[cid].clan[0] = 0;
|
||||
|
||||
/* reset input */
|
||||
for(i = 0; i < 200; i++)
|
||||
{
|
||||
clients[cid].inputs[i].game_tick = -1;
|
||||
clients[cid].inputs[i].pred_tick = -1;
|
||||
}
|
||||
clients[cid].current_input = 0;
|
||||
|
||||
snapstorage_purge_all(&clients[cid].snapshots);
|
||||
clients[cid].last_acked_snapshot = -1;
|
||||
return 0;
|
||||
|
@ -359,14 +403,14 @@ static void server_process_client_packet(NETPACKET *packet)
|
|||
int msg = msg_unpack_start(packet->data, packet->data_size, &sys);
|
||||
if(sys)
|
||||
{
|
||||
// system message
|
||||
/* system message */
|
||||
if(msg == NETMSG_INFO)
|
||||
{
|
||||
char version[64];
|
||||
strncpy(version, msg_unpack_string(), 64);
|
||||
if(strcmp(version, mods_net_version()) != 0)
|
||||
{
|
||||
// OH FUCK! wrong version, drop him
|
||||
/* OH FUCK! wrong version, drop him */
|
||||
char reason[256];
|
||||
sprintf(reason, "wrong version. server is running %s.", mods_net_version());
|
||||
netserver_drop(net, cid, reason);
|
||||
|
@ -377,7 +421,7 @@ static void server_process_client_packet(NETPACKET *packet)
|
|||
strncpy(clients[cid].clan, msg_unpack_string(), MAX_CLANNAME_LENGTH);
|
||||
const char *password = msg_unpack_string();
|
||||
const char *skin = msg_unpack_string();
|
||||
(void)password; // ignore these variables
|
||||
(void)password; /* ignore these variables */
|
||||
(void)skin;
|
||||
server_send_map(cid);
|
||||
}
|
||||
|
@ -392,12 +436,29 @@ static void server_process_client_packet(NETPACKET *packet)
|
|||
}
|
||||
else if(msg == NETMSG_INPUT)
|
||||
{
|
||||
int input[MAX_INPUT_SIZE];
|
||||
int tick = msg_unpack_int();
|
||||
int size = msg_unpack_int();
|
||||
int i;
|
||||
|
||||
CLIENT_INPUT *input = &clients[cid].inputs[clients[cid].current_input];
|
||||
input->timeleft = server_tick_start_time(tick)-time_get();
|
||||
input->pred_tick = tick;
|
||||
|
||||
if(tick < server_tick())
|
||||
{
|
||||
/* TODO: how should we handle this */
|
||||
dbg_msg("server", "input got in late for=%d cur=%d", tick, server_tick());
|
||||
tick = server_tick()+1;
|
||||
}
|
||||
|
||||
input->game_tick = tick;
|
||||
|
||||
for(i = 0; i < size/4; i++)
|
||||
input[i] = msg_unpack_int();
|
||||
mods_client_input(cid, input);
|
||||
input->data[i] = msg_unpack_int();
|
||||
|
||||
//time_get()
|
||||
clients[cid].current_input++;
|
||||
clients[cid].current_input %= 200;
|
||||
}
|
||||
else if(msg == NETMSG_SNAPACK)
|
||||
{
|
||||
|
@ -413,7 +474,7 @@ static void server_process_client_packet(NETPACKET *packet)
|
|||
}
|
||||
else
|
||||
{
|
||||
// game message
|
||||
/* game message */
|
||||
mods_message(msg, cid);
|
||||
}
|
||||
}
|
||||
|
@ -427,7 +488,7 @@ static void server_send_serverinfo(NETADDR4 *addr)
|
|||
packer_add_raw(&p, SERVERBROWSE_INFO, sizeof(SERVERBROWSE_INFO));
|
||||
packer_add_string(&p, config.sv_name, 128);
|
||||
packer_add_string(&p, config.sv_map, 128);
|
||||
packer_add_int(&p, netserver_max_clients(net)); // max_players
|
||||
packer_add_int(&p, netserver_max_clients(net)); /* max_players */
|
||||
int c = 0;
|
||||
int i;
|
||||
for(i = 0; i < MAX_CLIENTS; i++)
|
||||
|
@ -435,7 +496,7 @@ static void server_send_serverinfo(NETADDR4 *addr)
|
|||
if(!clients[i].state != SRVCLIENT_STATE_EMPTY)
|
||||
c++;
|
||||
}
|
||||
packer_add_int(&p, c); // num_players
|
||||
packer_add_int(&p, c); /* num_players */
|
||||
|
||||
packet.client_id = -1;
|
||||
packet.address = *addr;
|
||||
|
@ -461,13 +522,13 @@ static void server_pump_network()
|
|||
{
|
||||
netserver_update(net);
|
||||
|
||||
// process packets
|
||||
/* process packets */
|
||||
NETPACKET packet;
|
||||
while(netserver_recv(net, &packet))
|
||||
{
|
||||
if(packet.client_id == -1)
|
||||
{
|
||||
// stateless
|
||||
/* stateless */
|
||||
if(packet.data_size == sizeof(SERVERBROWSE_GETINFO) &&
|
||||
memcmp(packet.data, SERVERBROWSE_GETINFO, sizeof(SERVERBROWSE_GETINFO)) == 0)
|
||||
{
|
||||
|
@ -501,23 +562,23 @@ static int server_run()
|
|||
{
|
||||
biggest_snapshot = 0;
|
||||
|
||||
net_init(); // For Windows compatibility.
|
||||
net_init(); /* For Windows compatibility. */
|
||||
|
||||
snap_init_id();
|
||||
|
||||
// load map
|
||||
/* load map */
|
||||
if(!map_load(config.sv_map))
|
||||
{
|
||||
dbg_msg("server", "failed to load map. mapname='%s'", config.sv_map);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// start server
|
||||
/* start server */
|
||||
NETADDR4 bindaddr;
|
||||
|
||||
if(strlen(config.sv_bindaddr) && net_host_lookup(config.sv_bindaddr, config.sv_port, &bindaddr) != 0)
|
||||
{
|
||||
// sweet!
|
||||
/* sweet! */
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -536,19 +597,16 @@ static int server_run()
|
|||
|
||||
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)
|
||||
if(net_host_lookup(config.masterserver, MASTERSERVER_PORT, &master_server) != 0)
|
||||
{
|
||||
// TODO: fix me
|
||||
//master_server = netaddr4(0, 0, 0, 0, 0);
|
||||
/* TODO: fix me */
|
||||
/*master_server = netaddr4(0, 0, 0, 0, 0); */
|
||||
}
|
||||
|
||||
mods_init();
|
||||
dbg_msg("server", "version %s", mods_net_version());
|
||||
|
||||
int64 time_per_tick = time_freq()/SERVER_TICK_SPEED;
|
||||
int64 time_per_heartbeat = time_freq() * 30;
|
||||
int64 starttime = time_get();
|
||||
lasttick = starttime;
|
||||
lastheartbeat = 0;
|
||||
|
||||
int64 reporttime = time_get();
|
||||
|
@ -558,28 +616,48 @@ static int server_run()
|
|||
int64 snaptime = 0;
|
||||
int64 networktime = 0;
|
||||
int64 totaltime = 0;
|
||||
|
||||
game_start_time = time_get();
|
||||
|
||||
if(config.debug)
|
||||
dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024);
|
||||
dbg_msg("server", "baseline memory usage %dk", mem_allocated()/1024);
|
||||
|
||||
while(1)
|
||||
{
|
||||
int64 t = time_get();
|
||||
if(t-lasttick > time_per_tick)
|
||||
if(t > server_tick_start_time(current_tick+1))
|
||||
{
|
||||
/* apply new input */
|
||||
{
|
||||
int c, i;
|
||||
for(c = 0; c < MAX_CLIENTS; c++)
|
||||
{
|
||||
if(clients[c].state == SRVCLIENT_STATE_EMPTY)
|
||||
continue;
|
||||
for(i = 0; i < 200; i++)
|
||||
{
|
||||
if(clients[c].inputs[i].game_tick == server_tick())
|
||||
{
|
||||
mods_client_input(c, clients[c].inputs[i].data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* progress game */
|
||||
{
|
||||
int64 start = time_get();
|
||||
server_do_tick();
|
||||
simulationtime += time_get()-start;
|
||||
}
|
||||
|
||||
/* snap game */
|
||||
{
|
||||
int64 start = time_get();
|
||||
server_do_snap();
|
||||
snaptime += time_get()-start;
|
||||
}
|
||||
|
||||
lasttick += time_per_tick;
|
||||
}
|
||||
|
||||
if(config.sv_sendheartbeats)
|
||||
|
@ -652,7 +730,7 @@ int main(int argc, char **argv)
|
|||
|
||||
config_load(config_filename);
|
||||
|
||||
// parse arguments
|
||||
/* parse arguments */
|
||||
for(i = 1; i < argc; i++)
|
||||
config_set(argv[i]);
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ int snapshot_get_item_datasize(SNAPSHOT *snap, int index)
|
|||
|
||||
int snapshot_get_item_index(SNAPSHOT *snap, int key)
|
||||
{
|
||||
/* TODO: this should not be a linear search. very bad */
|
||||
/* TODO: OPT: this should not be a linear search. very bad */
|
||||
int i;
|
||||
for(i = 0; i < snap->num_items; i++)
|
||||
{
|
||||
|
@ -135,6 +135,8 @@ static void undiff_item(int *past, int *diff, int *out, int size)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: OPT: this should be made much faster
|
||||
int snapshot_create_delta(SNAPSHOT *from, SNAPSHOT *to, void *dstdata)
|
||||
{
|
||||
SNAPSHOT_DELTA *delta = (SNAPSHOT_DELTA *)dstdata;
|
||||
|
|
|
@ -39,6 +39,7 @@ struct client_data
|
|||
int team;
|
||||
int emoticon;
|
||||
int emoticon_start;
|
||||
player_core predicted;
|
||||
} client_datas[MAX_CLIENTS];
|
||||
|
||||
inline float frandom() { return rand()/(float)(RAND_MAX); }
|
||||
|
@ -137,6 +138,7 @@ void draw_sprite(float x, float y, float size)
|
|||
gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale);
|
||||
}
|
||||
|
||||
/*
|
||||
void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity)
|
||||
{
|
||||
vec2 pos = *inout_pos;
|
||||
|
@ -166,7 +168,7 @@ void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity)
|
|||
{
|
||||
*inout_pos = pos + vel;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
class damage_indicators
|
||||
{
|
||||
|
@ -309,7 +311,7 @@ public:
|
|||
particles[i].vel.y += particles[i].gravity*time_passed;
|
||||
particles[i].vel *= particles[i].friction;
|
||||
vec2 vel = particles[i].vel*time_passed;
|
||||
move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom());
|
||||
move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom(), NULL);
|
||||
particles[i].vel = vel* (1.0f/time_passed);
|
||||
particles[i].life += time_passed;
|
||||
particles[i].rot += time_passed * particles[i].rotspeed;
|
||||
|
@ -681,6 +683,75 @@ static void process_events(int s)
|
|||
must_process_events = false;
|
||||
}
|
||||
|
||||
static player_core predicted_prev_player;
|
||||
static player_core predicted_player;
|
||||
|
||||
extern "C" void modc_predict()
|
||||
{
|
||||
// repredict player
|
||||
{
|
||||
world_core world;
|
||||
int local_cid = -1;
|
||||
|
||||
// search for players
|
||||
for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
|
||||
{
|
||||
SNAP_ITEM item;
|
||||
const void *data = snap_get_item(SNAP_CURRENT, i, &item);
|
||||
|
||||
if(item.type == OBJTYPE_PLAYER)
|
||||
{
|
||||
const obj_player *player = (const obj_player *)data;
|
||||
client_datas[player->clientid].predicted.world = &world;
|
||||
world.players[player->clientid] = &client_datas[player->clientid].predicted;
|
||||
|
||||
client_datas[player->clientid].predicted.read(player);
|
||||
if(player->local)
|
||||
local_cid = player->clientid;
|
||||
}
|
||||
}
|
||||
|
||||
// predict
|
||||
for(int tick = client_tick(); tick <= client_predtick(); tick++)
|
||||
{
|
||||
// first calculate where everyone should move
|
||||
for(int c = 0; c < MAX_CLIENTS; c++)
|
||||
{
|
||||
if(!world.players[c])
|
||||
continue;
|
||||
|
||||
mem_zero(&world.players[c]->input, sizeof(world.players[c]->input));
|
||||
if(local_cid == c)
|
||||
{
|
||||
// apply player input
|
||||
int *input = client_get_input(tick);
|
||||
if(input)
|
||||
world.players[c]->input = *((player_input*)input);
|
||||
}
|
||||
|
||||
world.players[c]->tick();
|
||||
}
|
||||
|
||||
// move all players and quantize their data
|
||||
for(int c = 0; c < MAX_CLIENTS; c++)
|
||||
{
|
||||
if(!world.players[c])
|
||||
continue;
|
||||
|
||||
world.players[c]->move();
|
||||
world.players[c]->quantize();
|
||||
}
|
||||
}
|
||||
|
||||
// get the data from the local player
|
||||
if(local_cid != -1)
|
||||
{
|
||||
predicted_prev_player = predicted_player;
|
||||
predicted_player = *world.players[local_cid];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void modc_newsnapshot()
|
||||
{
|
||||
if(must_process_events)
|
||||
|
@ -945,7 +1016,7 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
|
|||
// first pass we draw the outline
|
||||
// second pass we draw the filling
|
||||
int outline = p==0 ? 1 : 0;
|
||||
int shift = charids[skin%16];
|
||||
int shift = skin;
|
||||
|
||||
for(int f = 0; f < 2; f++)
|
||||
{
|
||||
|
@ -1070,22 +1141,37 @@ void draw_round_rect(float x, float y, float w, float h, float r)
|
|||
gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right
|
||||
}
|
||||
|
||||
static void render_player(const obj_player *prev, const obj_player *player)
|
||||
static void render_player(const obj_player *prev_obj, const obj_player *player_obj)
|
||||
{
|
||||
if(player->health < 0) // dont render dead players
|
||||
obj_player prev;
|
||||
obj_player player;
|
||||
prev = *prev_obj;
|
||||
player = *player_obj;
|
||||
|
||||
if(player.health < 0) // dont render dead players
|
||||
return;
|
||||
|
||||
if(player.local)
|
||||
{
|
||||
// apply predicted results
|
||||
predicted_player.write(&player);
|
||||
predicted_prev_player.write(&prev);
|
||||
}
|
||||
|
||||
int skin = gametype == GAMETYPE_DM ? player->clientid : skinseed + player->team;
|
||||
int skin = charids[player.clientid];
|
||||
|
||||
if(gametype != GAMETYPE_DM)
|
||||
skin = player.team*9; // 0 or 9
|
||||
|
||||
vec2 direction = get_direction(player->angle);
|
||||
float angle = player->angle/256.0f;
|
||||
vec2 position = mix(vec2(prev->x, prev->y), vec2(player->x, player->y), client_intratick());
|
||||
vec2 direction = get_direction(player.angle);
|
||||
float angle = player.angle/256.0f;
|
||||
vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), client_intratick());
|
||||
|
||||
if(prev->health < 0) // Don't flicker from previous position
|
||||
position = vec2(player->x, player->y);
|
||||
if(prev.health < 0) // Don't flicker from previous position
|
||||
position = vec2(player.x, player.y);
|
||||
|
||||
bool stationary = player->vx < 1 && player->vx > -1;
|
||||
bool inair = col_check_point(player->x, player->y+16) == 0;
|
||||
bool stationary = player.vx < 1 && player.vx > -1;
|
||||
bool inair = col_check_point(player.x, player.y+16) == 0;
|
||||
|
||||
// evaluate animation
|
||||
float walk_time = fmod(position.x, 100.0f)/100.0f;
|
||||
|
@ -1099,26 +1185,26 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
else
|
||||
anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f);
|
||||
|
||||
if (player->weapon == WEAPON_HAMMER)
|
||||
if (player.weapon == WEAPON_HAMMER)
|
||||
{
|
||||
float a = clamp((client_tick()-player->attacktick+client_intratick())/10.0f, 0.0f, 1.0f);
|
||||
float a = clamp((client_tick()-player.attacktick+client_intratick())/10.0f, 0.0f, 1.0f);
|
||||
anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f);
|
||||
}
|
||||
if (player->weapon == WEAPON_NINJA)
|
||||
if (player.weapon == WEAPON_NINJA)
|
||||
{
|
||||
float a = clamp((client_tick()-player->attacktick+client_intratick())/40.0f, 0.0f, 1.0f);
|
||||
float a = clamp((client_tick()-player.attacktick+client_intratick())/40.0f, 0.0f, 1.0f);
|
||||
anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f);
|
||||
}
|
||||
|
||||
// draw hook
|
||||
if (prev->hook_active && player->hook_active)
|
||||
if (prev.hook_state>0 && player.hook_state>0)
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_GAME].id);
|
||||
gfx_quads_begin();
|
||||
//gfx_quads_begin();
|
||||
|
||||
vec2 pos = position;
|
||||
vec2 hook_pos = mix(vec2(prev->hook_x, prev->hook_y), vec2(player->hook_x, player->hook_y), client_intratick());
|
||||
vec2 hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), client_intratick());
|
||||
|
||||
float d = distance(pos, hook_pos);
|
||||
vec2 dir = normalize(pos-hook_pos);
|
||||
|
@ -1150,13 +1236,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
gfx_quads_setrotation(state.attach.angle*pi*2+angle);
|
||||
|
||||
// normal weapons
|
||||
int iw = clamp(player->weapon, 0, NUM_WEAPONS-1);
|
||||
int iw = clamp(player.weapon, 0, NUM_WEAPONS-1);
|
||||
select_sprite(data->weapons[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
|
||||
|
||||
vec2 dir = direction;
|
||||
float recoil = 0.0f;
|
||||
vec2 p;
|
||||
if (player->weapon == WEAPON_HAMMER)
|
||||
if (player.weapon == WEAPON_HAMMER)
|
||||
{
|
||||
// Static position for hammer
|
||||
p = position;
|
||||
|
@ -1173,7 +1259,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
}
|
||||
draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
|
||||
}
|
||||
else if (player->weapon == WEAPON_NINJA)
|
||||
else if (player.weapon == WEAPON_NINJA)
|
||||
{
|
||||
p = position;
|
||||
p.y += data->weapons[iw].offsety;
|
||||
|
@ -1190,13 +1276,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
|
||||
|
||||
// HADOKEN
|
||||
if ((client_tick()-player->attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites)
|
||||
if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites)
|
||||
{
|
||||
int itex = rand() % data->weapons[iw].nummuzzlesprites;
|
||||
float alpha = 1.0f;
|
||||
if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite)
|
||||
{
|
||||
vec2 dir = vec2(player->x,player->y) - vec2(prev->x, prev->y);
|
||||
vec2 dir = vec2(player.x,player.y) - vec2(prev.x, prev.y);
|
||||
dir = normalize(dir);
|
||||
float hadokenangle = atan(dir.y/dir.x);
|
||||
if (dir.x < 0.0f)
|
||||
|
@ -1216,7 +1302,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
{
|
||||
// TODO: should be an animation
|
||||
recoil = 0;
|
||||
float a = (client_tick()-player->attacktick+client_intratick())/5.0f;
|
||||
float a = (client_tick()-player.attacktick+client_intratick())/5.0f;
|
||||
if(a < 1)
|
||||
recoil = sinf(a*pi);
|
||||
p = position + dir * data->weapons[iw].offsetx - dir*recoil*10.0f;
|
||||
|
@ -1224,13 +1310,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
|
||||
}
|
||||
|
||||
if (player->weapon == WEAPON_GUN || player->weapon == WEAPON_SHOTGUN)
|
||||
if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN)
|
||||
{
|
||||
// check if we're firing stuff
|
||||
if (true)///prev->attackticks)
|
||||
if (true)///prev.attackticks)
|
||||
{
|
||||
float alpha = 0.0f;
|
||||
int phase1tick = (client_tick() - player->attacktick);
|
||||
int phase1tick = (client_tick() - player.attacktick);
|
||||
if (phase1tick < (data->weapons[iw].muzzleduration + 3))
|
||||
{
|
||||
float intratick = client_intratick();
|
||||
|
@ -1252,14 +1338,14 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
|
||||
/*gfx_quads_setcolor(1.0f,1.0f,1.0f,alpha);
|
||||
vec2 diry(-dir.y,dir.x);
|
||||
p += dir * muzzleparams[player->weapon].offsetx + diry * offsety;
|
||||
gfx_quads_draw(p.x,p.y,muzzleparams[player->weapon].sizex, muzzleparams[player->weapon].sizey);*/
|
||||
p += dir * muzzleparams[player.weapon].offsetx + diry * offsety;
|
||||
gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/
|
||||
}
|
||||
}
|
||||
}
|
||||
gfx_quads_end();
|
||||
|
||||
switch (player->weapon)
|
||||
switch (player.weapon)
|
||||
{
|
||||
case WEAPON_GUN: render_hand(skin, p, direction, -3*pi/4, vec2(-15, 4)); break;
|
||||
case WEAPON_SHOTGUN: render_hand(skin, p, direction, -pi/2, vec2(-5, 4)); break;
|
||||
|
@ -1269,9 +1355,15 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
}
|
||||
|
||||
// render the tee
|
||||
render_tee(&state, skin, player->emote, direction, position);
|
||||
if(player.local && config.debug)
|
||||
{
|
||||
vec2 ghost_position = mix(vec2(prev_obj->x, prev_obj->y), vec2(player_obj->x, player_obj->y), client_intratick());
|
||||
render_tee(&state, 15, player.emote, direction, ghost_position); // render ghost
|
||||
}
|
||||
|
||||
render_tee(&state, skin, player.emote, direction, position);
|
||||
|
||||
if(player->state == STATE_CHATTING)
|
||||
if(player.state == STATE_CHATTING)
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_CHAT_BUBBLES].id);
|
||||
gfx_quads_begin();
|
||||
|
@ -1280,13 +1372,13 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
gfx_quads_end();
|
||||
}
|
||||
|
||||
if (client_datas[player->clientid].emoticon_start != -1 && client_datas[player->clientid].emoticon_start + 2 * client_tickspeed() > client_tick())
|
||||
if (client_datas[player.clientid].emoticon_start != -1 && client_datas[player.clientid].emoticon_start + 2 * client_tickspeed() > client_tick())
|
||||
{
|
||||
gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
|
||||
gfx_quads_begin();
|
||||
|
||||
int since_start = client_tick() - client_datas[player->clientid].emoticon_start;
|
||||
int from_end = client_datas[player->clientid].emoticon_start + 2 * client_tickspeed() - client_tick();
|
||||
int since_start = client_tick() - client_datas[player.clientid].emoticon_start;
|
||||
int from_end = client_datas[player.clientid].emoticon_start + 2 * client_tickspeed() - client_tick();
|
||||
|
||||
float a = 1;
|
||||
|
||||
|
@ -1307,7 +1399,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
|
|||
|
||||
gfx_quads_setcolor(1.0f,1.0f,1.0f,a);
|
||||
// client_datas::emoticon is an offset from the first emoticon
|
||||
select_sprite(SPRITE_OOP + client_datas[player->clientid].emoticon);
|
||||
select_sprite(SPRITE_OOP + client_datas[player.clientid].emoticon);
|
||||
gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h);
|
||||
gfx_quads_end();
|
||||
}
|
||||
|
@ -1726,6 +1818,9 @@ void render_game()
|
|||
}
|
||||
}
|
||||
|
||||
local_player_pos = mix(predicted_prev_player.pos, predicted_player.pos, client_intratick());
|
||||
//local_player_pos = predicted_player.pos;
|
||||
|
||||
// everything updated, do events
|
||||
if(must_process_events)
|
||||
process_events(SNAP_PREV);
|
||||
|
|
|
@ -20,6 +20,80 @@ inline float get_angle(vec2 dir)
|
|||
return a;
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline T saturated_add(T min, T max, T current, T modifier)
|
||||
{
|
||||
if(modifier < 0)
|
||||
{
|
||||
if(current < min)
|
||||
return current;
|
||||
current += modifier;
|
||||
if(current < min)
|
||||
current = min;
|
||||
return current;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(current > max)
|
||||
return current;
|
||||
current += modifier;
|
||||
if(current > max)
|
||||
current = max;
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces);
|
||||
void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity);
|
||||
|
||||
|
||||
// hooking stuff
|
||||
enum
|
||||
{
|
||||
HOOK_RETRACTED=-1,
|
||||
HOOK_IDLE=0,
|
||||
HOOK_FLYING,
|
||||
HOOK_GRABBED
|
||||
};
|
||||
|
||||
class world_core
|
||||
{
|
||||
public:
|
||||
world_core()
|
||||
{
|
||||
mem_zero(players, sizeof(players));
|
||||
}
|
||||
|
||||
class player_core *players[MAX_CLIENTS];
|
||||
};
|
||||
|
||||
class player_core
|
||||
{
|
||||
public:
|
||||
world_core *world;
|
||||
|
||||
vec2 pos;
|
||||
vec2 vel;
|
||||
|
||||
vec2 hook_pos;
|
||||
vec2 hook_dir;
|
||||
int hook_tick;
|
||||
int hook_state;
|
||||
int hooked_player;
|
||||
|
||||
int jumped;
|
||||
player_input input;
|
||||
|
||||
void tick();
|
||||
void move();
|
||||
|
||||
void read(const obj_player_core *obj_core);
|
||||
void write(obj_player_core *obj_core);
|
||||
void quantize();
|
||||
};
|
||||
|
||||
|
||||
#define LERP(a,b,t) (a + (b-a) * t)
|
||||
#define min(a, b) ( a > b ? b : a)
|
||||
#define max(a, b) ( a > b ? a : b)
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
// NOTE: Be very careful when editing this file as it will change the network version
|
||||
|
||||
// --------- PHYSICS TWEAK! --------
|
||||
const float ground_control_speed = 7.0f;
|
||||
const float ground_control_accel = 2.0f;
|
||||
const float ground_friction = 0.5f;
|
||||
const float ground_jump_speed = 13.5f;
|
||||
const float air_control_speed = 3.5f;
|
||||
const float air_control_accel = 1.2f;
|
||||
const float air_friction = 0.95f;
|
||||
const float hook_length = 34*10.0f;
|
||||
const float hook_fire_speed = 45.0f;
|
||||
const float hook_drag_accel = 3.0f;
|
||||
const float hook_drag_speed = 15.0f;
|
||||
const float gravity = 0.5f;
|
||||
const float wall_friction = 0.80f;
|
||||
const float wall_jump_speed_up = ground_jump_speed*0.8f;
|
||||
const float wall_jump_speed_out = ground_jump_speed*0.8f;
|
||||
|
||||
// Network stuff
|
||||
enum
|
||||
{
|
||||
|
@ -129,7 +146,19 @@ struct obj_flag
|
|||
int team;
|
||||
};
|
||||
|
||||
struct obj_player
|
||||
|
||||
struct obj_player_core
|
||||
{
|
||||
int x, y;
|
||||
int vx, vy;
|
||||
int angle;
|
||||
|
||||
int hook_state;
|
||||
int hook_x, hook_y;
|
||||
int hook_dx, hook_dy;
|
||||
};
|
||||
|
||||
struct obj_player : public obj_player_core
|
||||
{
|
||||
int local;
|
||||
int clientid;
|
||||
|
@ -139,10 +168,6 @@ struct obj_player
|
|||
int armor;
|
||||
int ammocount;
|
||||
|
||||
int x, y;
|
||||
int vx, vy;
|
||||
int angle;
|
||||
|
||||
int weapon; // current active weapon
|
||||
|
||||
int attacktick; // num attack ticks left of current attack
|
||||
|
@ -152,7 +177,5 @@ struct obj_player
|
|||
int latency_flux;
|
||||
int emote;
|
||||
|
||||
int hook_active;
|
||||
int hook_x, hook_y;
|
||||
int team;
|
||||
};
|
||||
|
|
|
@ -14,6 +14,9 @@ MACRO_CONFIG_INT(scorelimit, 20, 0, 1000)
|
|||
MACRO_CONFIG_INT(timelimit, 0, 0, 1000)
|
||||
MACRO_CONFIG_STR(gametype, 32, "dm")
|
||||
|
||||
MACRO_CONFIG_INT(dbg_bots, 0, 0, 7)
|
||||
MACRO_CONFIG_INT(cl_predict, 1, 0, 1)
|
||||
|
||||
MACRO_CONFIG_INT(dynamic_camera, 1, 0, 1)
|
||||
|
||||
|
||||
|
|
|
@ -9,26 +9,6 @@
|
|||
|
||||
data_container *data = 0x0;
|
||||
|
||||
// --------- DEBUG STUFF ---------
|
||||
const int debug_bots = 3;
|
||||
|
||||
// --------- PHYSICS TWEAK! --------
|
||||
const float ground_control_speed = 7.0f;
|
||||
const float ground_control_accel = 2.0f;
|
||||
const float ground_friction = 0.5f;
|
||||
const float ground_jump_speed = 13.5f;
|
||||
const float air_control_speed = 3.5f;
|
||||
const float air_control_accel = 1.2f;
|
||||
const float air_friction = 0.95f;
|
||||
const float hook_length = 34*10.0f;
|
||||
const float hook_fire_speed = 45.0f;
|
||||
const float hook_drag_accel = 3.0f;
|
||||
const float hook_drag_speed = 15.0f;
|
||||
const float gravity = 0.5f;
|
||||
const float wall_friction = 0.80f;
|
||||
const float wall_jump_speed_up = ground_jump_speed*0.8f;
|
||||
const float wall_jump_speed_out = ground_jump_speed*0.8f;
|
||||
|
||||
class player* get_player(int index);
|
||||
void create_damageind(vec2 p, float angle_mod, int amount);
|
||||
void create_explosion(vec2 p, int owner, int weapon, bool bnodamage);
|
||||
|
@ -39,134 +19,6 @@ void create_sound(vec2 pos, int sound, int loopflags = 0);
|
|||
void create_targetted_sound(vec2 pos, int sound, int target, int loopflags = 0);
|
||||
class player *intersect_player(vec2 pos0, vec2 pos1, vec2 &new_pos, class entity *notthis = 0);
|
||||
|
||||
template<typename T>
|
||||
T saturated_add(T min, T max, T current, T modifier)
|
||||
{
|
||||
if(modifier < 0)
|
||||
{
|
||||
if(current < min)
|
||||
return current;
|
||||
current += modifier;
|
||||
if(current < min)
|
||||
current = min;
|
||||
return current;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(current > max)
|
||||
return current;
|
||||
current += modifier;
|
||||
if(current > max)
|
||||
current = max;
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rewrite this smarter!
|
||||
void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity, int *bounces)
|
||||
{
|
||||
if(bounces)
|
||||
*bounces = 0;
|
||||
|
||||
vec2 pos = *inout_pos;
|
||||
vec2 vel = *inout_vel;
|
||||
if(col_check_point(pos + vel))
|
||||
{
|
||||
int affected = 0;
|
||||
if(col_check_point(pos.x + vel.x, pos.y))
|
||||
{
|
||||
inout_vel->x *= -elasticity;
|
||||
if(bounces)
|
||||
(*bounces)++;
|
||||
affected++;
|
||||
}
|
||||
|
||||
if(col_check_point(pos.x, pos.y + vel.y))
|
||||
{
|
||||
inout_vel->y *= -elasticity;
|
||||
if(bounces)
|
||||
(*bounces)++;
|
||||
affected++;
|
||||
}
|
||||
|
||||
if(affected == 0)
|
||||
{
|
||||
inout_vel->x *= -elasticity;
|
||||
inout_vel->y *= -elasticity;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*inout_pos = pos + vel;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: rewrite this smarter!
|
||||
void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
|
||||
{
|
||||
// do the move
|
||||
vec2 pos = *inout_pos;
|
||||
vec2 vel = *inout_vel;
|
||||
|
||||
float distance = length(vel);
|
||||
int max = (int)distance;
|
||||
|
||||
vec2 offsets[4] = { vec2(-size.x/2, -size.y/2), vec2( size.x/2, -size.y/2),
|
||||
vec2(-size.x/2, size.y/2), vec2( size.x/2, size.y/2)};
|
||||
|
||||
if(distance > 0.00001f)
|
||||
{
|
||||
vec2 old_pos = pos;
|
||||
for(int i = 0; i <= max; i++)
|
||||
{
|
||||
float amount = i/(float)max;
|
||||
if(max == 0)
|
||||
amount = 0;
|
||||
|
||||
vec2 new_pos = pos + vel*amount; // TODO: this row is not nice
|
||||
|
||||
for(int p = 0; p < 4; p++)
|
||||
{
|
||||
vec2 np = new_pos+offsets[p];
|
||||
vec2 op = old_pos+offsets[p];
|
||||
if(col_check_point(np))
|
||||
{
|
||||
int affected = 0;
|
||||
if(col_check_point(np.x, op.y))
|
||||
{
|
||||
vel.x = -vel.x*elasticity;
|
||||
pos.x = old_pos.x;
|
||||
new_pos.x = old_pos.x;
|
||||
affected++;
|
||||
}
|
||||
|
||||
if(col_check_point(op.x, np.y))
|
||||
{
|
||||
vel.y = -vel.y*elasticity;
|
||||
pos.y = old_pos.y;
|
||||
new_pos.y = old_pos.y;
|
||||
affected++;
|
||||
}
|
||||
|
||||
if(!affected)
|
||||
{
|
||||
new_pos = old_pos;
|
||||
pos = old_pos;
|
||||
vel *= -elasticity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
old_pos = new_pos;
|
||||
}
|
||||
|
||||
pos = old_pos;
|
||||
}
|
||||
|
||||
*inout_pos = pos;
|
||||
*inout_vel = vel;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// Event handler
|
||||
//////////////////////////////////////////////////
|
||||
|
@ -784,8 +636,8 @@ void player::reset()
|
|||
release_hooks();
|
||||
|
||||
pos = vec2(0.0f, 0.0f);
|
||||
vel = vec2(0.0f, 0.0f);
|
||||
direction = vec2(0.0f, 1.0f);
|
||||
core.vel = vec2(0.0f, 0.0f);
|
||||
//direction = vec2(0.0f, 1.0f);
|
||||
score = 0;
|
||||
dead = true;
|
||||
spawning = false;
|
||||
|
@ -863,7 +715,9 @@ void player::try_respawn()
|
|||
|
||||
spawning = false;
|
||||
pos = spawnpos;
|
||||
defered_pos = pos;
|
||||
|
||||
core.pos = pos;
|
||||
core.hooked_player = -1;
|
||||
|
||||
|
||||
health = 10;
|
||||
|
@ -873,8 +727,10 @@ void player::try_respawn()
|
|||
set_flag(entity::FLAG_ALIVE);
|
||||
state = STATE_PLAYING;
|
||||
|
||||
core.hook_state = HOOK_IDLE;
|
||||
|
||||
mem_zero(&input, sizeof(input));
|
||||
vel = vec2(0.0f, 0.0f);
|
||||
core.vel = vec2(0.0f, 0.0f);
|
||||
|
||||
// init weapons
|
||||
mem_zero(&weapons, sizeof(weapons));
|
||||
|
@ -905,13 +761,14 @@ bool player::is_grounded()
|
|||
// releases the hooked player
|
||||
void player::release_hooked()
|
||||
{
|
||||
hook_state = HOOK_RETRACTED;
|
||||
hooked_player = 0x0;
|
||||
//hook_state = HOOK_RETRACTED;
|
||||
//hooked_player = 0x0;
|
||||
}
|
||||
|
||||
// release all hooks to this player
|
||||
void player::release_hooks()
|
||||
{
|
||||
/*
|
||||
// TODO: loop thru players only
|
||||
for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
|
||||
{
|
||||
|
@ -921,11 +778,13 @@ void player::release_hooks()
|
|||
if(p->hooked_player == this)
|
||||
p->release_hooked();
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
int player::handle_ninja()
|
||||
{
|
||||
vec2 direction = normalize(vec2(input.target_x, input.target_y));
|
||||
|
||||
if ((server_tick() - ninjaactivationtick) > (data->weapons[WEAPON_NINJA].duration * server_tickspeed() / 1000))
|
||||
{
|
||||
// time's up, return
|
||||
|
@ -956,18 +815,18 @@ int player::handle_ninja()
|
|||
if (currentmovetime == 0)
|
||||
{
|
||||
// reset player velocity
|
||||
vel *= 0.2f;
|
||||
core.vel *= 0.2f;
|
||||
//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
|
||||
}
|
||||
|
||||
if (currentmovetime > 0)
|
||||
{
|
||||
// Set player velocity
|
||||
vel = activationdir * data->weapons[WEAPON_NINJA].velocity;
|
||||
core.vel = activationdir * data->weapons[WEAPON_NINJA].velocity;
|
||||
vec2 oldpos = pos;
|
||||
move_box(&defered_pos, &vel, vec2(phys_size, phys_size), 0.0f);
|
||||
move_box(&core.pos, &core.vel, vec2(phys_size, phys_size), 0.0f);
|
||||
// reset velocity so the client doesn't predict stuff
|
||||
vel = vec2(0.0f,0.0f);
|
||||
core.vel = vec2(0.0f,0.0f);
|
||||
if ((currentmovetime % 2) == 0)
|
||||
{
|
||||
create_smoke(pos);
|
||||
|
@ -1015,6 +874,8 @@ int player::handle_ninja()
|
|||
|
||||
int player::handle_weapons()
|
||||
{
|
||||
vec2 direction = normalize(vec2(input.target_x, input.target_y));
|
||||
|
||||
if(config.stress)
|
||||
{
|
||||
for(int i = 0; i < NUM_WEAPONS; i++)
|
||||
|
@ -1175,7 +1036,7 @@ int player::handle_weapons()
|
|||
dir = normalize(target->pos - pos);
|
||||
else
|
||||
dir = vec2(0,-1);
|
||||
target->vel += dir * 25.0f + vec2(0,-5.0f);
|
||||
target->core.vel += dir * 25.0f + vec2(0,-5.0f);
|
||||
}
|
||||
}
|
||||
if (data->weapons[active_weapon].ammoregentime)
|
||||
|
@ -1228,7 +1089,6 @@ void player::tick()
|
|||
try_respawn();
|
||||
|
||||
// TODO: rework the input to be more robust
|
||||
// TODO: remove this tick count, it feels weird
|
||||
if(dead)
|
||||
{
|
||||
if(server_tick()-die_tick >= server_tickspeed()*5) // auto respawn after 3 sec
|
||||
|
@ -1238,205 +1098,30 @@ void player::tick()
|
|||
return;
|
||||
}
|
||||
|
||||
// fetch some info
|
||||
bool grounded = is_grounded();
|
||||
int wall_sliding = 0;
|
||||
direction = normalize(vec2(input.target_x, input.target_y));
|
||||
//player_core core;
|
||||
//core.pos = pos;
|
||||
//core.jumped = jumped;
|
||||
core.input = input;
|
||||
core.tick();
|
||||
|
||||
float max_speed = grounded ? ground_control_speed : air_control_speed;
|
||||
float accel = grounded ? ground_control_accel : air_control_accel;
|
||||
float friction = grounded ? ground_friction : air_friction;
|
||||
|
||||
if(!grounded && vel.y > 0)
|
||||
{
|
||||
if(input.left && col_check_point((int)(pos.x-phys_size/2)-4, (int)(pos.y)))
|
||||
wall_sliding = -1;
|
||||
if(input.right && col_check_point((int)(pos.x+phys_size/2)+4, (int)(pos.y)))
|
||||
wall_sliding = 1;
|
||||
}
|
||||
|
||||
if(wall_sliding)
|
||||
vel.y *= wall_friction;
|
||||
|
||||
// handle movement
|
||||
if(input.left)
|
||||
vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel);
|
||||
if(input.right)
|
||||
vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
|
||||
|
||||
if(!input.left && !input.right)
|
||||
vel.x *= friction;
|
||||
|
||||
// handle jumping
|
||||
if(input.jump)
|
||||
{
|
||||
if(!jumped && (grounded || wall_sliding))
|
||||
{
|
||||
create_sound(pos, SOUND_PLAYER_JUMP);
|
||||
if(wall_sliding)
|
||||
{
|
||||
vel.y = -wall_jump_speed_up;
|
||||
vel.x = -wall_jump_speed_out*wall_sliding;
|
||||
}
|
||||
else
|
||||
vel.y = -ground_jump_speed;
|
||||
jumped++;
|
||||
}
|
||||
}
|
||||
else
|
||||
jumped = 0;
|
||||
|
||||
// do hook
|
||||
if(input.hook)
|
||||
{
|
||||
if(hook_state == HOOK_IDLE)
|
||||
{
|
||||
hook_state = HOOK_FLYING;
|
||||
hook_pos = pos;
|
||||
hook_dir = direction;
|
||||
hook_tick = -1;
|
||||
}
|
||||
else if(hook_state == HOOK_FLYING)
|
||||
{
|
||||
vec2 new_pos = hook_pos+hook_dir*hook_fire_speed;
|
||||
|
||||
// Check against other players first
|
||||
for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
|
||||
{
|
||||
if(ent && ent->objtype == OBJTYPE_PLAYER)
|
||||
{
|
||||
player *p = (player*)ent;
|
||||
if(p != this && !p->dead && distance(p->pos, new_pos) < p->phys_size)
|
||||
{
|
||||
hook_state = HOOK_GRABBED;
|
||||
hooked_player = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(hook_state == HOOK_FLYING)
|
||||
{
|
||||
// check against ground
|
||||
if(col_intersect_line(hook_pos, new_pos, &new_pos))
|
||||
{
|
||||
hook_state = HOOK_GRABBED;
|
||||
hook_pos = new_pos;
|
||||
}
|
||||
else if(distance(pos, new_pos) > hook_length)
|
||||
{
|
||||
hook_state = HOOK_RETRACTED;
|
||||
}
|
||||
else
|
||||
hook_pos = new_pos;
|
||||
}
|
||||
|
||||
if(hook_state == HOOK_GRABBED)
|
||||
{
|
||||
create_sound(pos, SOUND_HOOK_ATTACH);
|
||||
hook_tick = server_tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
release_hooked();
|
||||
hook_state = HOOK_IDLE;
|
||||
hook_pos = pos;
|
||||
}
|
||||
|
||||
if(hook_state == HOOK_GRABBED)
|
||||
{
|
||||
if(hooked_player)
|
||||
{
|
||||
hook_pos = hooked_player->pos;
|
||||
|
||||
// keep players hooked for a max of 1.5sec
|
||||
if(server_tick() > hook_tick+(server_tickspeed()*3)/2)
|
||||
release_hooked();
|
||||
}
|
||||
|
||||
/*if(hooked_player)
|
||||
hook_pos = hooked_player->pos;
|
||||
|
||||
float d = distance(pos, hook_pos);
|
||||
vec2 dir = normalize(pos - hook_pos);
|
||||
if(d > 10.0f) // TODO: fix tweakable variable
|
||||
{
|
||||
float accel = hook_drag_accel * (d/hook_length);
|
||||
vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x*0.75f);
|
||||
vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y);
|
||||
}*/
|
||||
|
||||
// Old version feels much better (to me atleast)
|
||||
if(distance(hook_pos, pos) > 46.0f)
|
||||
{
|
||||
vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel;
|
||||
// the hook as more power to drag you up then down.
|
||||
// this makes it easier to get on top of an platform
|
||||
if(hookvel.y > 0)
|
||||
hookvel.y *= 0.3f;
|
||||
|
||||
// the hook will boost it's power if the player wants to move
|
||||
// in that direction. otherwise it will dampen everything abit
|
||||
if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right))
|
||||
hookvel.x *= 0.95f;
|
||||
else
|
||||
hookvel.x *= 0.75f;
|
||||
|
||||
vec2 new_vel = vel+hookvel;
|
||||
|
||||
// check if we are under the legal limit for the hook
|
||||
if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
|
||||
vel = new_vel; // no problem. apply
|
||||
}
|
||||
}
|
||||
|
||||
// fix influence of other players, collision + hook
|
||||
// TODO: loop thru players only
|
||||
for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
|
||||
{
|
||||
if(ent && ent->objtype == OBJTYPE_PLAYER)
|
||||
{
|
||||
player *p = (player*)ent;
|
||||
if(p == this || !(p->flags&FLAG_ALIVE))
|
||||
continue; // make sure that we don't nudge our self
|
||||
|
||||
// handle player <-> player collision
|
||||
float d = distance(pos, p->pos);
|
||||
vec2 dir = normalize(pos - p->pos);
|
||||
if(d < phys_size*1.25f)
|
||||
{
|
||||
float a = phys_size*1.25f - d;
|
||||
vel = vel + dir*a;
|
||||
}
|
||||
|
||||
// handle hook influence
|
||||
if(p->hooked_player == this)
|
||||
{
|
||||
if(d > phys_size*1.50f) // TODO: fix tweakable variable
|
||||
{
|
||||
float accel = hook_drag_accel * (d/hook_length);
|
||||
vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x);
|
||||
vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle weapons
|
||||
int retflags = handle_weapons();
|
||||
/*
|
||||
if (!(retflags & (MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION)))
|
||||
{
|
||||
// add gravity
|
||||
if (!(retflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
|
||||
vel.y += gravity;
|
||||
//if (!(retflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
|
||||
//vel.y += gravity;
|
||||
|
||||
// do the move
|
||||
defered_pos = pos;
|
||||
move_box(&defered_pos, &vel, vec2(phys_size, phys_size), 0);
|
||||
}
|
||||
move_box(&core.pos, &vel, vec2(phys_size, phys_size), 0);
|
||||
}*/
|
||||
|
||||
//defered_pos = core.pos;
|
||||
//jumped = core.jumped;
|
||||
|
||||
state = input.state;
|
||||
|
||||
// Previnput
|
||||
|
@ -1446,8 +1131,12 @@ void player::tick()
|
|||
|
||||
void player::tick_defered()
|
||||
{
|
||||
core.move();
|
||||
core.quantize();
|
||||
pos = core.pos;
|
||||
|
||||
// apply the new position
|
||||
pos = defered_pos;
|
||||
//pos = defered_pos;
|
||||
}
|
||||
|
||||
void player::die(int killer, int weapon)
|
||||
|
@ -1478,13 +1167,14 @@ void player::die(int killer, int weapon)
|
|||
|
||||
bool player::take_damage(vec2 force, int dmg, int from, int weapon)
|
||||
{
|
||||
vel += force;
|
||||
core.vel += force;
|
||||
|
||||
// player only inflicts half damage on self
|
||||
if(from == client_id)
|
||||
dmg = max(1, dmg/2);
|
||||
|
||||
if (gameobj->gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team)
|
||||
// CTF and TDM,
|
||||
if (gameobj->gametype != GAMETYPE_DM && from >= 0 && players[from].team == team)
|
||||
return false;
|
||||
|
||||
damage_taken++;
|
||||
|
@ -1567,10 +1257,15 @@ void player::snap(int snaping_client)
|
|||
{
|
||||
obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player));
|
||||
|
||||
player->x = (int)pos.x;
|
||||
player->y = (int)pos.y;
|
||||
player->vx = (int)vel.x;
|
||||
player->vy = (int)vel.y;
|
||||
core.write(player);
|
||||
|
||||
if(snaping_client != client_id)
|
||||
{
|
||||
player->vx = 0; // make sure that we don't send these to clients who don't need them
|
||||
player->vy = 0;
|
||||
player->hook_dx = 0;
|
||||
player->hook_dy = 0;
|
||||
}
|
||||
|
||||
if (emote_stop < server_tick())
|
||||
{
|
||||
|
@ -1613,21 +1308,6 @@ void player::snap(int snaping_client)
|
|||
player->emote = EMOTE_BLINK;
|
||||
}
|
||||
|
||||
player->hook_active = hook_state>0?1:0;
|
||||
player->hook_x = (int)hook_pos.x;
|
||||
player->hook_y = (int)hook_pos.y;
|
||||
|
||||
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;
|
||||
|
||||
player->angle = (int)(a*256.0f);
|
||||
|
||||
player->score = score;
|
||||
player->team = team;
|
||||
|
||||
|
@ -2011,7 +1691,7 @@ void mods_tick()
|
|||
if(world.paused) // make sure that the game object always updates
|
||||
gameobj->tick();
|
||||
|
||||
if(debug_bots)
|
||||
if(config.dbg_bots)
|
||||
{
|
||||
static int count = 0;
|
||||
if(count >= 0)
|
||||
|
@ -2019,13 +1699,14 @@ void mods_tick()
|
|||
count++;
|
||||
if(count == 10)
|
||||
{
|
||||
for(int i = 0; i < debug_bots ; i++)
|
||||
for(int i = 0; i < config.dbg_bots ; i++)
|
||||
{
|
||||
mods_client_enter(MAX_CLIENTS-i-1);
|
||||
strcpy(players[MAX_CLIENTS-i-1].name, "(bot)");
|
||||
if(gameobj->gametype != GAMETYPE_DM)
|
||||
players[MAX_CLIENTS-i-1].team = count&1;
|
||||
players[MAX_CLIENTS-i-1].team = i&1;
|
||||
}
|
||||
|
||||
count = -1;
|
||||
}
|
||||
}
|
||||
|
@ -2187,7 +1868,15 @@ void mods_init()
|
|||
|
||||
players = new player[MAX_CLIENTS];
|
||||
gameobj = new gameobject;
|
||||
|
||||
// setup core world
|
||||
for(int i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
players[i].core.world = &world.core;
|
||||
world.core.players[i] = &players[i].core;
|
||||
}
|
||||
|
||||
//
|
||||
int start, num;
|
||||
map_get_type(MAPRES_ITEM, &start, &num);
|
||||
|
||||
|
|
|
@ -85,6 +85,8 @@ public:
|
|||
bool paused;
|
||||
bool reset_requested;
|
||||
|
||||
world_core core;
|
||||
|
||||
game_world();
|
||||
int find_entities(vec2 pos, float radius, entity **ents, int max);
|
||||
int find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes);
|
||||
|
@ -229,9 +231,9 @@ public:
|
|||
int last_action;
|
||||
|
||||
// we need a defered position so we can handle the physics correctly
|
||||
vec2 defered_pos;
|
||||
vec2 vel;
|
||||
vec2 direction;
|
||||
//vec2 defered_pos;
|
||||
//vec2 vel;
|
||||
//vec2 direction;
|
||||
|
||||
//
|
||||
int client_id;
|
||||
|
@ -269,21 +271,14 @@ public:
|
|||
int latency_avg;
|
||||
int latency_min;
|
||||
int latency_max;
|
||||
|
||||
// hooking stuff
|
||||
enum
|
||||
{
|
||||
HOOK_RETRACTED=-1,
|
||||
HOOK_IDLE=0,
|
||||
HOOK_FLYING,
|
||||
HOOK_GRABBED
|
||||
};
|
||||
|
||||
int hook_state;
|
||||
int hook_tick;
|
||||
player *hooked_player;
|
||||
vec2 hook_pos;
|
||||
vec2 hook_dir;
|
||||
player_core core;
|
||||
|
||||
//int hook_state;
|
||||
//int hook_tick;
|
||||
//player *hooked_player;
|
||||
//vec2 hook_pos;
|
||||
//vec2 hook_dir;
|
||||
|
||||
//
|
||||
player();
|
||||
|
|
|
@ -38,8 +38,8 @@ int run(int port, NETADDR4 dest)
|
|||
if(bytes <= 0)
|
||||
break;
|
||||
|
||||
if((rand()%10) == 0) // drop the packet
|
||||
continue;
|
||||
//if((rand()%10) == 0) // drop the packet
|
||||
// continue;
|
||||
|
||||
// create new packet
|
||||
packet *p = (packet *)mem_alloc(sizeof(packet)+bytes, 1);
|
||||
|
@ -100,13 +100,13 @@ int run(int port, NETADDR4 dest)
|
|||
net_udp4_send(socket, &p->send_to, p->data, p->data_size);
|
||||
|
||||
// update lag
|
||||
double flux = rand()/(double)RAND_MAX;
|
||||
double flux = 0; //rand()/(double)RAND_MAX;
|
||||
int ms_spike = 0;
|
||||
int ms_flux = 20;
|
||||
int ms_ping = 20;
|
||||
int ms_flux = 00;
|
||||
int ms_ping = 100;
|
||||
current_latency = ((time_freq()*ms_ping)/1000) + (int64)(((time_freq()*ms_flux)/1000)*flux); // 50ms
|
||||
|
||||
if((p->id%100) == 0)
|
||||
if(ms_spike && (p->id%100) == 0)
|
||||
current_latency += (time_freq()*ms_spike)/1000;
|
||||
|
||||
mem_free(p);
|
||||
|
|
35
src/tools/map_resave.c
Normal file
35
src/tools/map_resave.c
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <engine/datafile.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int i, id, type, size;
|
||||
void *ptr;
|
||||
DATAFILE *df;
|
||||
DATAFILE_OUT *df_out;
|
||||
|
||||
if(argc != 3)
|
||||
return -1;
|
||||
|
||||
df = datafile_load(argv[1]);
|
||||
df_out = datafile_create(argv[2]);
|
||||
|
||||
/* add all items */
|
||||
for(i = 0; i < datafile_num_items(df); i++)
|
||||
{
|
||||
ptr = datafile_get_item(df, i, &type, &id);
|
||||
size = datafile_get_itemsize(df, i);
|
||||
datafile_add_item(df_out, type, id, size, ptr);
|
||||
}
|
||||
|
||||
/* add all data */
|
||||
for(i = 0; i < datafile_num_data(df); i++)
|
||||
{
|
||||
ptr = datafile_get_data(df, i);
|
||||
size = datafile_get_datasize(df, i);
|
||||
datafile_add_data(df_out, size, ptr);
|
||||
}
|
||||
|
||||
datafile_unload(df);
|
||||
datafile_finish(df_out);
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue