From 421fc71813f08bfe359f9ac7596933a7e4cea6e0 Mon Sep 17 00:00:00 2001
From: Stijn Buys <ingar@osirion.org>
Date: Wed, 7 May 2008 18:56:00 +0000
Subject: client-side frame interpolation: network updates, interpolation of
 position

---
 ROADMAP                    |  32 ++++++++++++++++++++++----------
 TODO                       |  11 ++++++++++-
 osirion.kdevelop.pcs       | Bin 639076 -> 653766 bytes
 osirion.kdevses            |  21 ++++++---------------
 src/client/camera.cc       |   8 +++++++-
 src/core/commandbuffer.cc  |  45 ++++++++++++++++++++++++++++++++++++++++++++-
 src/core/commandbuffer.h   |   3 +++
 src/core/gameconnection.cc |   7 ++++---
 src/core/gameinterface.cc  |  28 ++++++++++------------------
 src/core/gameinterface.h   |   4 ++--
 src/core/gameserver.cc     |   2 +-
 src/core/netserver.cc      |   2 +-
 src/render/draw.cc         |  30 ++++++++++++++++--------------
 13 files changed, 126 insertions(+), 67 deletions(-)

diff --git a/ROADMAP b/ROADMAP
index 39f07ec..6241459 100644
--- a/ROADMAP
+++ b/ROADMAP
@@ -1,6 +1,6 @@
 ROADMAP
 
-* MILESTONE 1
+* MILESTONE 1 - version 0.1
 
 Description:
 	The game takes place in a simple solar system with one star,
@@ -12,32 +12,44 @@ Description:
 	they can use private chat or global chat.
 
 Requires:
-	Network subsystem
+	Client console
+	Entities
 	Ship instances
+	Network subsystem
 	Entities
+	Camera handling
+	Keyboard bindings
 
+* MILESTONE 2 - version 0.2
 
-* MILESTONE 2
+Description:
 	Players can shoot at each other. They can crash into the star 
 	or the planet.
 
 Requires:
-	weapons
-	turret and cannon models
-	
+	Events: explosions, weapons fire
+	Model weapon support
+	Turret and cannon models
+	Particle systems
 	clip brushes and collision detection
 
+* MILESTONE 3 - version 0.3
 
-* MILESTONE 3
+Description:
 	Players can dock at the space station and buy a ship or purchase
 	weapons.
 
 Requires:
-	mechanisms for docking
-	gui
+	Docking
+	Docking GUI
 
-* MILESTONE 4
+* MILESTONE 4 - version 0.4
 
 
 * MILESTONE 5
 
+* Release 1.0
+
+Requires:
+	Stable network protocol
+
diff --git a/TODO b/TODO
index 728bd34..c93629e 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,13 @@
 TODO
 
+milestone 1:
+        console text color and wrapping
+	entity axis interpolation
+	camera axis interpolation
+	camera tracking mode
+	camera frustum clip
+	keyboard binds
+
 filesystem:
 	write a filesystem based on streams
 	write handlers for zip
@@ -16,6 +24,7 @@ core:
 	parse command line options (ok)
 	execute command line options (ok)
 	globe entity (ok)
+	execute config files
 	game module loading/unloading
 
 network:
@@ -29,7 +38,7 @@ network:
 	zlib compression
 	fix lag
 	protocol version in handshake
-
+	detect and disconnect clients behaving badly
 client:
 	input handler switching (ok)
 	console chars (ok)
diff --git a/osirion.kdevelop.pcs b/osirion.kdevelop.pcs
index c991dc8..d35692e 100644
Binary files a/osirion.kdevelop.pcs and b/osirion.kdevelop.pcs differ
diff --git a/osirion.kdevses b/osirion.kdevses
index 2f81c5c..d014ce3 100644
--- a/osirion.kdevses
+++ b/osirion.kdevses
@@ -1,25 +1,16 @@
 <?xml version = '1.0' encoding = 'UTF-8'?>
 <!DOCTYPE KDevPrjSession>
 <KDevPrjSession>
- <DocsAndViews NumberOfDocuments="6" >
-  <Doc0 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/gameconnection.cc" >
-   <View0 Encoding="" line="44" Type="Source" />
+ <DocsAndViews NumberOfDocuments="3" >
+  <Doc0 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/netconnection.cc" >
+   <View0 Encoding="" line="0" Type="Source" />
   </Doc0>
-  <Doc1 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/gameserver.cc" >
-   <View0 Encoding="" line="313" Type="Source" />
+  <Doc1 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/gameconnection.cc" >
+   <View0 Encoding="" line="95" Type="Source" />
   </Doc1>
-  <Doc2 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/entity.cc" >
+  <Doc2 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/gameserver.cc" >
    <View0 Encoding="" line="0" Type="Source" />
   </Doc2>
-  <Doc3 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/netconnection.cc" >
-   <View0 Encoding="" line="330" Type="Source" />
-  </Doc3>
-  <Doc4 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/entity.h" >
-   <View0 Encoding="" line="44" Type="Source" />
-  </Doc4>
-  <Doc5 NumberOfViews="1" URL="file:///home/ingar/projects/osirion/osirion-work/src/core/netserver.cc" >
-   <View0 Encoding="" line="277" Type="Source" />
-  </Doc5>
  </DocsAndViews>
  <pluginList>
   <kdevdebugger>
diff --git a/src/client/camera.cc b/src/client/camera.cc
index 808ff7e..573194a 100644
--- a/src/client/camera.cc
+++ b/src/client/camera.cc
@@ -169,7 +169,13 @@ void draw(float seconds)
 		if (mode == Overview)
 			set_mode(Track);
 
-		target.assign(core::localcontrol()->location());
+		if (core::localcontrol()->state()) {
+			// client prediction has not been run yet
+			target = core::localcontrol()->state()->previouslocation() 
+				+ (core::localcontrol()->location() - core::localcontrol()->state()->previouslocation()) * core::game()->timeoffset();
+		}
+		else
+			target = core::localcontrol()->location();
 			
 		if (mode == Track) {
  			if (core::localcontrol()->model())
diff --git a/src/core/commandbuffer.cc b/src/core/commandbuffer.cc
index ead2a98..56aba16 100644
--- a/src/core/commandbuffer.cc
+++ b/src/core/commandbuffer.cc
@@ -5,10 +5,12 @@
 */
 
 #include <string>
+#include <fstream>
 #include <sstream>
 #include <list>
 
 #include "sys/sys.h"
+#include "filesystem/filesystem.h"
 #include "core/application.h"
 #include "core/commandbuffer.h"
 #include "core/gameconnection.h"
@@ -59,6 +61,16 @@ void func_set(std::string const &args)
 	return;
 }
 
+void func_exec(std::string const &args)
+{
+	std::istringstream argstream(args);
+	std::string filename;
+	if (!(argstream >> filename))
+			return;
+
+	CommandBuffer::exec_file(filename);	
+}
+
 std::stringstream CommandBuffer::cmdbuf(std::stringstream::in | std::stringstream::out);
 
 void CommandBuffer::init()
@@ -77,6 +89,9 @@ void CommandBuffer::init()
 
 	func = Func::add("set", (FuncPtr)func_set);
 	func->set_info("[variable] [str] set variable value");
+
+	func = Func::add("exec", (FuncPtr)exec);
+	func->set_info("[filename] execute commands from file");
 }
 
 void CommandBuffer::shutdown()
@@ -142,7 +157,7 @@ void  CommandBuffer::exec(std::string const &cmdline)
 		return;
 	}
 	
-	// TODO this must get forwarded to the server
+	// this gets forwarded to the server
 	if (connection()) 
 		connection()->forward(cmdline);
 	else
@@ -219,5 +234,33 @@ void CommandBuffer::complete(std::string &input, size_t &pos)
 	
 }
 
+void CommandBuffer::exec_file(std::string const & filename)
+{
+        filesystem::File *f = filesystem::open(filename.c_str());
+        if (!f) {
+                con_warn << "Could not open " << filename << std::endl;
+                return;
+        }
+
+        std::string fn = f->path();
+        fn.append(f->name());
+        filesystem::close(f);
+
+	std::ifstream ifs;
+	ifs.open(fn.c_str());
+        if (!ifs.is_open()) {
+                con_warn << "Could not stream " << fn << "!\n";
+                return;
+        }
+        
+	char line[MAXCMDSIZE];
+        while (ifs.getline(line, MAXCMDSIZE-1)) {
+                if (line[0] && line[0] != '#' && line[0] != ';')
+			exec(std::string(line));
+        }
+
+	ifs.close();
+}
+
 }
 
diff --git a/src/core/commandbuffer.h b/src/core/commandbuffer.h
index 8970d89..7b26211 100644
--- a/src/core/commandbuffer.h
+++ b/src/core/commandbuffer.h
@@ -28,6 +28,9 @@ public:
 	/// clear the command buffer
 	static void clear();
 
+	/// execute commands from a file
+	static void exec_file(std::string const & filename);
+
 	/// global buffer to hold the command stream
 	static std::stringstream cmd;
 
diff --git a/src/core/gameconnection.cc b/src/core/gameconnection.cc
index baf0967..b5ac27d 100644
--- a/src/core/gameconnection.cc
+++ b/src/core/gameconnection.cc
@@ -89,20 +89,21 @@ void GameConnection::frame(float seconds)
 	}
 
 	if (!Cvar::sv_dedicated->value()) {
-		update_clientstate();
+		update_clientstate(seconds);
 	}
 
+	connection_network->frame(seconds);
+
 	connection_frametime += seconds;
 
 	float f = 0;
 	if (core::Cvar::sv_framerate->value()) {		
-		f =  1.0f / core::Cvar::sv_framerate->value();
+		f =  0.5f / core::Cvar::sv_framerate->value();
 		if (connection_frametime < f) {
 			return;
 		}
 	}
 			
-	connection_network->frame(connection_frametime);
 	connection_frametime = 0;
 
 	if (localcontrol() && localcontrol()->dirty()) {
diff --git a/src/core/gameinterface.cc b/src/core/gameinterface.cc
index 8d351a0..d1bc1c3 100644
--- a/src/core/gameinterface.cc
+++ b/src/core/gameinterface.cc
@@ -95,48 +95,40 @@ void GameInterface::clear()
 	game_previousframetime = 0;
 	game_serverframetime = 0;
 	game_clientframetime = 0;
-	game_timestep = 0;
-	game_frames = 0;
 }
 
 void GameInterface::reset_clientstate(float servertime)
 {
-	game_timestep =  (servertime - game_serverframetime);
-
-	if (game_timestep < 0)
-		game_timestep = 0;
-
 	game_previousframetime = game_serverframetime;
-	game_serverframetime = servertime;
-	game_clientframetime = game_previousframetime;
+	game_clientframetime = game_serverframetime;
 
+	game_serverframetime = servertime;
+	
 	std::map<unsigned int, core::Entity *>::iterator it;
 	for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) {
 		
 		core::Entity *entity = (*it).second;
 
-		if (entity->state() && !(entity->flags() & Entity::Static))
+		if (entity->state() && !(entity->flags() & core::Entity::Static))
 			entity->state()->assign(entity);
 	}
-	
-	game_frames = 0;
 }
 
-void GameInterface::update_clientstate()
+void GameInterface::update_clientstate(float seconds)
 {
-	game_frames++;
-	game_clientframetime += game_timestep;
+	game_clientframetime += seconds;
 
 	if (game_clientframetime > game_serverframetime)
 		game_clientframetime = game_serverframetime;
 }
 
 float GameInterface::timeoffset() {
+
 	float d = game_serverframetime - game_previousframetime;
-	if (d < 0)
-		d = 1;
-	float t = game_serverframetime - game_clientframetime;
+	if (d <= 0)
+		return 0;
 
+	float t = game_clientframetime - game_previousframetime;
 	return t/d;
 }
 
diff --git a/src/core/gameinterface.h b/src/core/gameinterface.h
index 2324bb5..0edcb1b 100644
--- a/src/core/gameinterface.h
+++ b/src/core/gameinterface.h
@@ -55,7 +55,7 @@ public:
 	void reset_clientstate(float servertime);
 
 	/// update the client state timers
-	void update_clientstate();
+	void update_clientstate(float seconds);
 
 /*----- virtual mutators ------------------------------------------ */
 	
@@ -73,7 +73,7 @@ protected:
 	float				game_timestep;
 	float				game_clientframetime;
 
-	unsigned int			game_frames;
+	unsigned int			game_serverframelength;
 };
 
 /// global local player instance
diff --git a/src/core/gameserver.cc b/src/core/gameserver.cc
index 3c126fc..3e5241e 100644
--- a/src/core/gameserver.cc
+++ b/src/core/gameserver.cc
@@ -260,7 +260,7 @@ void GameServer::frame(float seconds)
 	 	localplayer()->update_info();
 
 	if (!Cvar::sv_dedicated->value()) {
-		update_clientstate();
+		update_clientstate(seconds);
 	}
 
 	server_frametime += seconds;
diff --git a/src/core/netserver.cc b/src/core/netserver.cc
index 3005922..e66f63d 100644
--- a/src/core/netserver.cc
+++ b/src/core/netserver.cc
@@ -179,7 +179,7 @@ void NetServer::receive()
 
 	timeval timeout;
 	timeout.tv_sec = 0;
-	timeout.tv_usec = 5000;
+	timeout.tv_usec = 2500;
 	fd_set readset = serverset;
 
 	int nb = select(fd()+1, &readset, NULL, NULL, &timeout);
diff --git a/src/render/draw.cc b/src/render/draw.cc
index b0671de..26655e0 100644
--- a/src/render/draw.cc
+++ b/src/render/draw.cc
@@ -238,12 +238,7 @@ void pass_prepare()
 	for (it=core::Entity::registry.begin(); it != core::Entity::registry.end(); it++) {
 		
 		core::Entity *entity = (*it).second;
-		if (!entity->state()) {
-			entity->entity_clientstate = new core::ClientState();
-		}
-		entity->state()->state_visible = false;
-		entity->state()->state_detailvisible = false;
-
+		
 		// load entity models and light flare textures
 		if (!entity->model() && entity->modelname().size()) {
 			entity->entity_model = model::Model::load(entity->modelname());
@@ -263,7 +258,14 @@ void pass_prepare()
 		}
 
 		// update client state
-		if (entity->state() && flag_is_set(entity->flags(), core::Entity::Static)) {
+		if (!entity->state()) {
+			entity->entity_clientstate = new core::ClientState();
+			entity->state()->assign(entity);
+		}
+		entity->state()->state_visible = false;
+		entity->state()->state_detailvisible = false;
+
+		if (!flag_is_set(entity->flags(), core::Entity::Static)) {
 			entity->state()->state_location = entity->state()->previouslocation() + 
 			(entity->location() - entity->state()->previouslocation()) * core::game()->timeoffset();
 		}
@@ -310,7 +312,7 @@ void draw_pass_default()
 		// draw entities without model
 		if (!entity->model()) {
 			gl::push();
-			gl::translate(entity->location());
+			gl::translate(entity->state()->location());
 			gl::multmatrix(entity->axis());
 
 			if (flag_is_set(entity->flags(), core::Entity::Bright)) {
@@ -353,7 +355,7 @@ void draw_pass_model_vertex()
 		core::Entity *entity = (*it).second;
 		if (entity->model() && entity->state()->visible()) {
 			gl::push();
-			gl::translate(entity->location());
+			gl::translate(entity->state()->location());
 			gl::multmatrix(entity->axis());
 
 			draw_model_vertex(entity);
@@ -373,7 +375,7 @@ void draw_pass_model_evertex()
 
 		if (entity->model() && entity->state()->visible()) {
 			gl::push();
-			gl::translate(entity->location());
+			gl::translate(entity->state()->location());
 			gl::multmatrix(entity->axis());
 
 			draw_model_evertex(entity);	
@@ -395,7 +397,7 @@ void draw_pass_model_shields() {
 		
 			if (entity->type() == core::Entity::Controlable) {
 				gl::push();
-				gl::translate(entity->location());
+				gl::translate(entity->state()->location());
 				gl::multmatrix(entity->axis());
 
 				draw_model_shield((core::EntityControlable *)entity);
@@ -432,7 +434,7 @@ void draw_pass_model_fx()
 					t = (core::application()->time() + entity->state()->fuzz() + (*lit)->offset()) * (*lit)->frequency();
 
 				if (!(*lit)->strobe() || (( t - floorf(t)) <= (*lit)->time())) {
-					math::Vector3f location = entity->location() + (entity->axis() * (*lit)->location());
+					math::Vector3f location = entity->state()->location() + (entity->axis() * (*lit)->location());
 					float light_size = 0.0625 * (*lit)->radius();
 	
 					if ((*lit)->render_texture != flare_texture) {
@@ -483,7 +485,7 @@ void draw_pass_model_fx()
 				t = 0.75f + t/2;
 				
 				for(std::list<model::Engine*>::iterator eit = entity->model()->model_engine.begin(); eit != entity->model()->model_engine.end(); eit++) {
-					math::Vector3f location = entity->location() + (entity->axis() * (*eit)->location());
+					math::Vector3f location = entity->state()->location() + (entity->axis() * (*eit)->location());
 					float engine_size = 0.0625 * (*eit)->radius() * t;
 
 					math::Color color(1.0f, 0.0f, 0.0f, 0.9f * u);
@@ -518,7 +520,7 @@ void draw_pass_model_corona()
 
 		if (entity->state()->visible() && (entity->shape() != core::Entity::Sphere)) {
 			gl::push();
-			gl::translate(entity->location());
+			gl::translate(entity->state()->location());
 			math::Color color = entity->color();
 			color.a = 0.25f;
 			draw_sphere(color, entity->model()->radius());
-- 
cgit v1.2.3