about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-02-10 15:32:30 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-02-10 15:32:30 +0000
commit294cbe18c17cd20f70f965e7f89a3250ef39b990 (patch)
treedb942402bd39bfb8336e2a2d134f96f2ad5b2ab1 /src
parentacffe66ebe63f3ecc970db0feab33a6e85a7d1f4 (diff)
downloadzcatch-294cbe18c17cd20f70f965e7f89a3250ef39b990.tar.gz
zcatch-294cbe18c17cd20f70f965e7f89a3250ef39b990.zip
done some more latency work. added the extra message for projectiles to reduce latency
Diffstat (limited to 'src')
-rw-r--r--src/engine/client/ec_client.c32
-rw-r--r--src/engine/e_if_mods.h16
-rw-r--r--src/engine/e_if_server.h1
-rw-r--r--src/engine/e_network.c5
-rw-r--r--src/engine/e_network.h1
-rw-r--r--src/engine/e_protocol.h2
-rw-r--r--src/engine/e_system.c22
-rw-r--r--src/engine/e_system.h1
-rw-r--r--src/engine/server/es_server.c19
-rw-r--r--src/game/client/gc_client.cpp2
-rw-r--r--src/game/client/gc_client.h14
-rw-r--r--src/game/client/gc_console.cpp4
-rw-r--r--src/game/client/gc_hooks.cpp28
-rw-r--r--src/game/client/gc_render.cpp12
-rw-r--r--src/game/client/gc_render_obj.cpp7
-rw-r--r--src/game/g_protocol.h2
-rw-r--r--src/game/g_version.h4
-rw-r--r--src/game/server/gs_common.h11
-rw-r--r--src/game/server/gs_server.cpp186
19 files changed, 330 insertions, 39 deletions
diff --git a/src/engine/client/ec_client.c b/src/engine/client/ec_client.c
index 627791bb..dfc16ec3 100644
--- a/src/engine/client/ec_client.c
+++ b/src/engine/client/ec_client.c
@@ -60,6 +60,9 @@ static int snapcrcerrors = 0;
 static int ack_game_tick = -1;
 static int current_recv_tick = 0;
 
+/* pinging */
+static int64 ping_start_time = 0;
+
 /* current time */
 static int current_tick = 0;
 static float intratick = 0;
@@ -687,6 +690,14 @@ static void client_process_packet(NETPACKET *packet)
 					client_disconnect_with_reason(error);
 				}
 			}
+			else if(msg == NETMSG_PING)
+			{
+				msg_pack_start_system(NETMSG_PING_REPLY, 0);
+				msg_pack_end();
+				client_send_msg();
+			}
+			else if(msg == NETMSG_PING_REPLY)
+				dbg_msg("client/network", "latency %.2f", (time_get() - ping_start_time)*1000 / (float)time_freq());
 			else if(msg == NETMSG_SNAP || msg == NETMSG_SNAPSINGLE || msg == NETMSG_SNAPEMPTY)
 			{
 				/*dbg_msg("client/network", "got snapshot"); */
@@ -1209,28 +1220,37 @@ static void client_run()
 	snd_shutdown();
 }
 
-static void connect_command(void *result, void *user_data)
+static void con_connect(void *result, void *user_data)
 {
 	const char *address;
 	console_result_string(result, 1, &address);
 	client_connect(address);
 }
 
-static void disconnect_command(void *result, void *user_data)
+static void con_disconnect(void *result, void *user_data)
 {
 	client_disconnect();
 }
 
-static void quit_command(void *result, void *user_data)
+static void con_quit(void *result, void *user_data)
 {
 	client_quit();
 }
 
+static void con_ping(void *result, void *user_data)
+{
+	msg_pack_start_system(NETMSG_PING, 0);
+	msg_pack_end();
+	client_send_msg();
+	ping_start_time = time_get();
+}
+
 static void client_register_commands()
 {
-	MACRO_REGISTER_COMMAND("quit", "", quit_command, 0x0);
-	MACRO_REGISTER_COMMAND("connect", "s", connect_command, 0x0);
-	MACRO_REGISTER_COMMAND("disconnect", "", disconnect_command, 0x0);
+	MACRO_REGISTER_COMMAND("quit", "", con_quit, 0x0);
+	MACRO_REGISTER_COMMAND("connect", "s", con_connect, 0x0);
+	MACRO_REGISTER_COMMAND("disconnect", "", con_disconnect, 0x0);
+	MACRO_REGISTER_COMMAND("ping", "", con_ping, 0x0);
 }
 
 int editor_main(int argc, char **argv);
diff --git a/src/engine/e_if_mods.h b/src/engine/e_if_mods.h
index eff909f3..08d0ec37 100644
--- a/src/engine/e_if_mods.h
+++ b/src/engine/e_if_mods.h
@@ -51,7 +51,7 @@ void mods_client_enter(int cid);
 void mods_client_drop(int cid);
 
 /*
-	Function: mods_client_input
+	Function: mods_client_direct_input
 		Called when the server recives new input from a client.
 
 	Arguments:
@@ -59,7 +59,19 @@ void mods_client_drop(int cid);
 		input - Pointer to the input data.
 		size - Size of the data. (NOT IMPLEMENTED YET)
 */
-void mods_client_input(int cid, void *input);
+void mods_client_direct_input(int cid, void *input);
+
+/*
+	Function: mods_client_predicted_input
+		Called when the server applys the predicted input on the client.
+
+	Arguments:
+		cid - Client ID. Is 0 - MAX_CLIENTS.
+		input - Pointer to the input data.
+		size - Size of the data. (NOT IMPLEMENTED YET)
+*/
+void mods_client_predicted_input(int cid, void *input);
+
 
 /*
 	Function: mods_tick
diff --git a/src/engine/e_if_server.h b/src/engine/e_if_server.h
index c9572b76..c9775d90 100644
--- a/src/engine/e_if_server.h
+++ b/src/engine/e_if_server.h
@@ -36,6 +36,7 @@ int server_getclientinfo(int client_id, CLIENT_INFO *info);
 const char *server_clientname(int client_id);
 
 /* grabs the latest input for the client. not withholding anything */
+
 /*
 	Function: server_latestinput
 		TODO
diff --git a/src/engine/e_network.c b/src/engine/e_network.c
index 689e45ab..3e242618 100644
--- a/src/engine/e_network.c
+++ b/src/engine/e_network.c
@@ -799,6 +799,11 @@ void netserver_stats(NETSERVER *s, NETSTATS *stats)
 	}
 }
 
+NETSOCKET netserver_socket(NETSERVER *s)
+{
+	return s->socket;
+}
+
 int netserver_client_addr(NETSERVER *s, int client_id, NETADDR4 *addr)
 {
 	*addr = s->slots[client_id].conn.peeraddr;
diff --git a/src/engine/e_network.h b/src/engine/e_network.h
index 96a49692..29843849 100644
--- a/src/engine/e_network.h
+++ b/src/engine/e_network.h
@@ -46,6 +46,7 @@ int netserver_recv(NETSERVER *s, NETPACKET *packet);
 int netserver_send(NETSERVER *s, NETPACKET *packet);
 int netserver_close(NETSERVER *s);
 int netserver_update(NETSERVER *s);
+NETSOCKET netserver_socket(NETSERVER *s);
 int netserver_drop(NETSERVER *s, int client_id, const char *reason);
 int netserver_client_addr(NETSERVER *s, int client_id, NETADDR4 *addr);
 int netserver_max_clients(NETSERVER *s);
diff --git a/src/engine/e_protocol.h b/src/engine/e_protocol.h
index 0e8a63f4..65d76350 100644
--- a/src/engine/e_protocol.h
+++ b/src/engine/e_protocol.h
@@ -46,6 +46,8 @@ enum
 	NETMSG_CMD,
 	
 	/* sent by both */
+	NETMSG_PING,
+	NETMSG_PING_REPLY,
 	NETMSG_ERROR
 };
 
diff --git a/src/engine/e_system.c b/src/engine/e_system.c
index 1472b3d6..ab7323f9 100644
--- a/src/engine/e_system.c
+++ b/src/engine/e_system.c
@@ -733,6 +733,28 @@ void swap_endian(void *data, unsigned elem_size, unsigned num)
 	}
 }
 
+int net_socket_read_wait(NETSOCKET sock, int time)
+{
+#if defined(CONF_FAMILY_WINDOWS)
+	#error Not implemented
+#else
+    struct timeval tv;
+    fd_set readfds;
+
+    tv.tv_sec = 0;
+    tv.tv_usec = 1000*time;
+
+    FD_ZERO(&readfds);
+    FD_SET(sock, &readfds);
+
+    /* don't care about writefds and exceptfds */
+    select(sock+1, &readfds, NULL, NULL, &tv);
+    if(FD_ISSET(sock, &readfds))
+    	return 1;
+    return 0;
+#endif
+}
+
 #if defined(__cplusplus)
 }
 #endif
diff --git a/src/engine/e_system.h b/src/engine/e_system.h
index 69fa5c63..f271d71a 100644
--- a/src/engine/e_system.h
+++ b/src/engine/e_system.h
@@ -512,6 +512,7 @@ int fs_listdir(const char *dir, fs_listdir_callback cb, void *user);
 int fs_storage_path(const char *appname, char *path, int max);
 int fs_makedir(const char *path);
 int net_addr4_cmp(const NETADDR4 *a, const NETADDR4 *b);
+int net_socket_read_wait(NETSOCKET sock, int time);
 
 void mem_debug_dump();
 int mem_allocated();
diff --git a/src/engine/server/es_server.c b/src/engine/server/es_server.c
index 8d805e42..9c2aac41 100644
--- a/src/engine/server/es_server.c
+++ b/src/engine/server/es_server.c
@@ -642,6 +642,9 @@ static void server_process_client_packet(NETPACKET *packet)
 			
 			clients[cid].current_input++;
 			clients[cid].current_input %= 200;
+		
+			/* call the mod with the fresh input data */
+			mods_client_direct_input(cid, clients[cid].latestinput.data);
 		}
 		else if(msg == NETMSG_CMD)
 		{
@@ -653,6 +656,12 @@ static void server_process_client_packet(NETPACKET *packet)
 				console_execute(cmd);
 			}
 		}
+		else if(msg == NETMSG_PING)
+		{
+			msg_pack_start_system(NETMSG_PING_REPLY, 0);
+			msg_pack_end();
+			server_send_msg(cid);
+		}
 		else
 		{
 			dbg_msg("server", "strange message cid=%d msg=%d data_size=%d", cid, msg, packet->data_size);
@@ -948,7 +957,7 @@ static int server_run()
 						{
 							if(clients[c].inputs[i].game_tick == server_tick())
 							{
-								mods_client_input(c, clients[c].inputs[i].data);
+								mods_client_predicted_input(c, clients[c].inputs[i].data);
 								break;
 							}
 						}
@@ -986,7 +995,7 @@ static int server_run()
 					lastheartbeat = t+time_per_heartbeat;
 				}
 			}
-	
+
 			{
 				static PERFORMACE_INFO scope = {"net", 0};
 				perf_start(&scope);
@@ -1025,6 +1034,10 @@ static int server_run()
 				config.sv_status = 0;
 			}
 			
+			/* wait for incomming data */
+			net_socket_read_wait(netserver_socket(net), 5);
+	
+			/*
 			if(config.dbg_hitch)
 			{
 				thread_sleep(config.dbg_hitch);
@@ -1032,7 +1045,7 @@ static int server_run()
 			}
 			else
 				thread_sleep(1);
-			
+			*/
 		}
 	}
 
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index 7032637f..d891ec84 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -25,7 +25,7 @@ extern "C" {
 #include "gc_console.h"
 
 struct data_container *data = 0;
-static int64 debug_firedelay = 0;
+int64 debug_firedelay = 0;
 
 player_input input_data = {0};
 int input_target_lock = 0;
diff --git a/src/game/client/gc_client.h b/src/game/client/gc_client.h
index 63c74d77..857a9088 100644
--- a/src/game/client/gc_client.h
+++ b/src/game/client/gc_client.h
@@ -36,6 +36,20 @@ extern int picked_up_weapon;
 extern player_input input_data;
 extern int input_target_lock;
 
+// debug
+extern int64 debug_firedelay;
+
+// extra projs
+enum
+{
+	MAX_EXTRA_PROJECTILES=32,
+};
+
+extern obj_projectile extraproj_projectiles[MAX_EXTRA_PROJECTILES];
+extern int extraproj_num;
+
+void extraproj_reset();
+
 // chat
 enum
 {
diff --git a/src/game/client/gc_console.cpp b/src/game/client/gc_console.cpp
index c04477a1..1f596e79 100644
--- a/src/game/client/gc_console.cpp
+++ b/src/game/client/gc_console.cpp
@@ -265,14 +265,14 @@ void console_render()
 
 	gfx_texture_set(data->images[IMAGE_CONSOLE_BG].id);
     gfx_quads_begin();
-    gfx_setcolor(0.2f, 0.2f, 0.2f,0.8f);
+    gfx_setcolor(0.2f, 0.2f, 0.2f,0.9f);
     gfx_quads_setsubset(0,-console_height*0.075f,screen.w*0.075f*0.5f,0);
     gfx_quads_drawTL(0,0,screen.w,console_height);
     gfx_quads_end();
 
 	gfx_texture_set(data->images[IMAGE_CONSOLE_BAR].id);
     gfx_quads_begin();
-    gfx_setcolor(1.0f, 1.0f, 1.0f, 0.8f);
+    gfx_setcolor(1.0f, 1.0f, 1.0f, 0.9f);
     gfx_quads_setsubset(0,0.1f,screen.w*0.015f,1-0.1f);
     gfx_quads_drawTL(0,console_height-10.0f,screen.w,10.0f);
     gfx_quads_end();
diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp
index f04af5cb..4ab5cf6a 100644
--- a/src/game/client/gc_hooks.cpp
+++ b/src/game/client/gc_hooks.cpp
@@ -394,6 +394,15 @@ extern "C" void modc_statechange(int state, int old)
 	}
 }
 
+
+obj_projectile extraproj_projectiles[MAX_EXTRA_PROJECTILES];
+int extraproj_num;
+
+void extraproj_reset()
+{
+	extraproj_num = 0;
+}
+
 extern "C" void modc_message(int msg)
 {
 	if(msg == MSG_CHAT)
@@ -409,6 +418,23 @@ extern "C" void modc_message(int msg)
 		else
 			snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0);
 	}
+	else if(msg == MSG_EXTRA_PROJECTILE)
+	{
+		int num = msg_unpack_int();
+		
+		for(int k = 0; k < num; k++)
+		{
+			obj_projectile proj;
+			for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+				((int *)&proj)[i] = msg_unpack_int();
+				
+			if(extraproj_num != MAX_EXTRA_PROJECTILES)
+			{
+				extraproj_projectiles[extraproj_num] = proj;
+				extraproj_num++;
+			}
+		}
+	}
 	else if(msg == MSG_SETINFO)
 	{
 		int cid = msg_unpack_int();
@@ -487,8 +513,8 @@ extern "C" void modc_connected()
 	
 	//tilemap_init();
 	chat_reset();
-
 	particle_reset();
+	extraproj_reset();
 	
 	clear_object_pointers();
 	last_new_predicted_tick = -1;
diff --git a/src/game/client/gc_render.cpp b/src/game/client/gc_render.cpp
index f201a87c..1d276ec3 100644
--- a/src/game/client/gc_render.cpp
+++ b/src/game/client/gc_render.cpp
@@ -370,6 +370,18 @@ static void render_items()
 				render_flag((const obj_flag *)prev, (const obj_flag *)data);
 		}
 	}
+
+	// render extra projectiles	
+	for(int i = 0; i < extraproj_num; i++)
+	{
+		if(extraproj_projectiles[i].start_tick < client_tick())
+		{
+			extraproj_projectiles[i] = extraproj_projectiles[extraproj_num-1];
+			extraproj_num--;
+		}
+		else
+			render_projectile(&extraproj_projectiles[i], 0);
+	}
 }
 
 
diff --git a/src/game/client/gc_render_obj.cpp b/src/game/client/gc_render_obj.cpp
index 39705444..cedfc129 100644
--- a/src/game/client/gc_render_obj.cpp
+++ b/src/game/client/gc_render_obj.cpp
@@ -13,13 +13,12 @@
 
 void render_projectile(const obj_projectile *current, int itemid)
 {
-	/*
 	if(debug_firedelay)
 	{
-		debug_firedelay = time_get()-debug_firedelay;
-		dbg_msg("game", "firedelay=%.2f ms", debug_firedelay/(float)time_freq()*1000.0f);
+		int64 delay = time_get()-debug_firedelay;
+		dbg_msg("game", "firedelay=%.2f ms", delay/(float)time_freq()*1000.0f);
 		debug_firedelay = 0;
-	}*/
+	}
 	
 	gfx_texture_set(data->images[IMAGE_GAME].id);
 	gfx_quads_begin();
diff --git a/src/game/g_protocol.h b/src/game/g_protocol.h
index ada685bb..f4878729 100644
--- a/src/game/g_protocol.h
+++ b/src/game/g_protocol.h
@@ -44,6 +44,8 @@ enum
     MSG_SOUND_GLOBAL,
     MSG_TUNE_PARAMS,
 	MSG_KILL,
+    MSG_EXTRA_PROJECTILE, // server -> client
+	
 };
 
 enum
diff --git a/src/game/g_version.h b/src/game/g_version.h
index d77410dd..c10af7af 100644
--- a/src/game/g_version.h
+++ b/src/game/g_version.h
@@ -1,4 +1,4 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 #include "generated/nethash.c"
-#define TEEWARS_VERSION "0.3.3"
-#define TEEWARS_NETVERSION "0.3 " TEEWARS_NETVERSION_HASH
+#define TEEWARS_VERSION "0.4.0-dev"
+#define TEEWARS_NETVERSION "0.4 " TEEWARS_NETVERSION_HASH
diff --git a/src/game/server/gs_common.h b/src/game/server/gs_common.h
index 838b192f..194751b2 100644
--- a/src/game/server/gs_common.h
+++ b/src/game/server/gs_common.h
@@ -186,11 +186,6 @@ public:
 	enum
 	{
 		PROJECTILE_FLAGS_EXPLODE = 1 << 0,
-
-		WEAPON_PROJECTILETYPE_GUN		= 0,
-		WEAPON_PROJECTILETYPE_ROCKET	= 1,
-		WEAPON_PROJECTILETYPE_SHOTGUN	= 2,
-		WEAPON_PROJECTILETYPE_SNIPER	= 6,
 	};
 	
 	vec2 vel;
@@ -208,6 +203,9 @@ public:
 	
 	projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner,
 		int damage, int flags, float force, int sound_impact, int weapon);
+
+	void fill_info(obj_projectile *proj);
+
 	virtual void reset();
 	virtual void tick();
 	virtual void snap(int snapping_client);
@@ -333,6 +331,9 @@ public:
 	
 	int handle_weapons();
 	int handle_ninja();
+	
+	void on_direct_input(player_input *input);
+	void fire_weapon();
 
 	virtual void tick();
 	virtual void tick_defered();
diff --git a/src/game/server/gs_server.cpp b/src/game/server/gs_server.cpp
index b9f78996..bcab00e5 100644
--- a/src/game/server/gs_server.cpp
+++ b/src/game/server/gs_server.cpp
@@ -437,6 +437,16 @@ void projectile::tick()
 	}
 }
 
+void projectile::fill_info(obj_projectile *proj)
+{
+	proj->x = (int)pos.x;
+	proj->y = (int)pos.y;
+	proj->vx = (int)vel.x;
+	proj->vy = (int)vel.y;
+	proj->start_tick = start_tick;
+	proj->type = type;
+}
+
 void projectile::snap(int snapping_client)
 {
 	float ct = (server_tick()-start_tick)/(float)server_tickspeed();
@@ -446,12 +456,7 @@ void projectile::snap(int snapping_client)
 		return;
 
 	obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile));
-	proj->x = (int)pos.x;
-	proj->y = (int)pos.y;
-	proj->vx = (int)vel.x;
-	proj->vy = (int)vel.y;
-	proj->start_tick = start_tick;
-	proj->type = type;
+	fill_info(proj);
 }
 
 
@@ -940,6 +945,138 @@ int player::handle_ninja()
 	return 0;
 }
 
+void player::fire_weapon()
+{
+	if(reload_timer != 0)
+		return;
+	
+	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
+	
+	bool fullauto = false;
+	if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN)
+		fullauto = true;
+
+	//if(count_input(latest_previnput.fire, latest_input.fire).presses) || ((fullauto && latest_input.fire&1) && weapons[active_weapon].ammo))
+	if(!count_input(latest_previnput.fire, latest_input.fire).presses)
+		return;
+	
+	// check for ammo
+	if(!weapons[active_weapon].ammo)
+	{
+		create_sound(pos, SOUND_WEAPON_NOAMMO);
+		return;
+	}
+
+	
+	switch(active_weapon)
+	{
+		case WEAPON_HAMMER:
+		{
+			// reset objects hit
+			numobjectshit = 0;
+			create_sound(pos, SOUND_HAMMER_FIRE);
+			break;
+		}
+
+		case WEAPON_GUN:
+		{
+			projectile *proj = new projectile(WEAPON_GUN,
+				client_id,
+				pos+vec2(0,0),
+				direction*tuning.gun_speed,
+				server_tickspeed(),
+				this,
+				1, 0, 0, -1, WEAPON_GUN);
+				
+			// pack the projectile and send it to the client directly
+			obj_projectile p;
+			proj->fill_info(&p);
+			
+			msg_pack_start(MSG_EXTRA_PROJECTILE, 0);
+			msg_pack_int(1);
+			for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+				msg_pack_int(((int *)&p)[i]);
+			msg_pack_end();
+			server_send_msg(client_id);
+							
+			create_sound(pos, SOUND_GUN_FIRE);
+			break;
+		}
+		case WEAPON_GRENADE:
+		{
+			projectile *proj = new projectile(WEAPON_GRENADE,
+				client_id,
+				pos+vec2(0,0),
+				direction*tuning.grenade_speed,
+				100,
+				this,
+				1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, SOUND_GRENADE_EXPLODE, WEAPON_GRENADE);
+
+			// pack the projectile and send it to the client directly
+			obj_projectile p;
+			proj->fill_info(&p);
+			
+			msg_pack_start(MSG_EXTRA_PROJECTILE, 0);
+			msg_pack_int(1);
+			for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+				msg_pack_int(((int *)&p)[i]);
+			msg_pack_end();
+			server_send_msg(client_id);
+
+			create_sound(pos, SOUND_GRENADE_FIRE);
+			break;
+		}
+		case WEAPON_SHOTGUN:
+		{
+			int shotspread = 2;
+
+			msg_pack_start(MSG_EXTRA_PROJECTILE, 0);
+			msg_pack_int(shotspread*2+1);
+			
+			for(int i = -shotspread; i <= shotspread; i++)
+			{
+				float spreading[] = {-0.185f, -0.070f, 0, 0.070f, 0.185f};
+				float a = get_angle(direction);
+				float v = 1.0f-fabs(i/(float)shotspread);
+				a += spreading[i+2];
+				float speed = mix((float)tuning.shotgun_speed_wide, (float)tuning.shotgun_speed_center, v);
+				projectile *proj = new projectile(WEAPON_SHOTGUN,
+					client_id,
+					pos+vec2(0,0),
+					vec2(cosf(a), sinf(a))*speed,
+					(int)(server_tickspeed()*0.25f),
+					this,
+					1, 0, 0, -1, WEAPON_SHOTGUN);
+					
+				// pack the projectile and send it to the client directly
+				obj_projectile p;
+				proj->fill_info(&p);
+				
+				for(unsigned i = 0; i < sizeof(obj_projectile)/sizeof(int); i++)
+					msg_pack_int(((int *)&p)[i]);
+			}
+
+			msg_pack_end();
+			server_send_msg(client_id);					
+			
+			create_sound(pos, SOUND_SHOTGUN_FIRE);
+			break;
+		}
+		
+		case WEAPON_RIFLE:
+		{
+			new laser(pos, direction, tuning.laser_reach, this);
+			create_sound(pos, SOUND_RIFLE_FIRE);
+			break;
+		}
+		
+	}
+
+	weapons[active_weapon].ammo--;
+	attack_tick = server_tick();
+	reload_timer = data->weapons[active_weapon].firedelay * server_tickspeed() / 1000;
+}
+
 int player::handle_weapons()
 {
 	vec2 direction = normalize(vec2(latest_input.target_x, latest_input.target_y));
@@ -1011,8 +1148,10 @@ int player::handle_weapons()
 		}
 	}
 
-	if(reload_timer == 0)
-	{
+	//if(reload_timer == 0)
+	fire_weapon();
+	//}
+	/*
 		bool fullauto = false;
 		if(active_weapon == WEAPON_GRENADE || active_weapon == WEAPON_SHOTGUN)
 			fullauto = true;
@@ -1096,7 +1235,7 @@ int player::handle_weapons()
 				create_sound(pos, SOUND_WEAPON_NOAMMO);
 			}
 		}
-	}
+	}*/
 
 	// Update weapons
 	if (active_weapon == WEAPON_HAMMER && reload_timer > 0)
@@ -1178,11 +1317,19 @@ int player::handle_weapons()
 	return 0;
 }
 
+void player::on_direct_input(player_input *new_input)
+{
+	mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
+	mem_copy(&latest_input, new_input, sizeof(latest_input));
+	fire_weapon();
+}
+
 void player::tick()
 {
 	server_setclientscore(client_id, score);
 
 	// grab latest input
+	/*
 	{
 		int size = 0;
 		int *input = server_latestinput(client_id, &size);
@@ -1191,7 +1338,7 @@ void player::tick()
 			mem_copy(&latest_previnput, &latest_input, sizeof(latest_input));
 			mem_copy(&latest_input, input, sizeof(latest_input));
 		}
-	}
+	}*/
 	
 	// check if we have enough input
 	// this is to prevent initial weird clicks
@@ -1434,7 +1581,7 @@ void player::snap(int snaping_client)
 	{
 		obj_player_info *info = (obj_player_info *)snap_new_item(OBJTYPE_PLAYER_INFO, client_id, sizeof(obj_player_info));
 
-		info->latency = latency_avg;
+		info->latency = latency_min;
 		info->latency_flux = latency_max-latency_min;
 		info->local = 0;
 		info->clientid = client_id;
@@ -1892,7 +2039,21 @@ void mods_snap(int client_id)
 	events.snap(client_id);
 }
 
-void mods_client_input(int client_id, void *input)
+void mods_client_direct_input(int client_id, void *input)
+{
+	if(!world->paused)
+		players[client_id].on_direct_input((player_input *)input);
+	
+	/*
+	if(i->fire)
+	{
+		msg_pack_start(MSG_EXTRA_PROJECTILE, 0);
+		msg_pack_end();
+		server_send_msg(client_id);
+	}*/
+}
+
+void mods_client_predicted_input(int client_id, void *input)
 {
 	if(!world->paused)
 	{
@@ -1905,7 +2066,6 @@ void mods_client_input(int client_id, void *input)
 		
 		if(players[client_id].input.target_x == 0 && players[client_id].input.target_y == 0)
 			players[client_id].input.target_y = -1;
-			
 	}
 }