r2951 - in trunk/data/qcsrc: common menu-div0test server

DONOTREPLY at icculus.org DONOTREPLY at icculus.org
Thu Nov 15 05:30:27 EST 2007


Author: div0
Date: 2007-11-15 05:30:27 -0500 (Thu, 15 Nov 2007)
New Revision: 2951

Added:
   trunk/data/qcsrc/common/mapinfo.qc
   trunk/data/qcsrc/common/mapinfo.qh
Modified:
   trunk/data/qcsrc/menu-div0test/mbuiltin.qh
   trunk/data/qcsrc/menu-div0test/menu.qc
   trunk/data/qcsrc/menu-div0test/progs.src
   trunk/data/qcsrc/server/progs.src
Log:
start of mapinfo system; beware: menu-div0test now starts up longer for the first time, and you may need to delete .nexuiz/data/data/maps/*.mapinfo when there are trouble with maplist later, as the mapinfo format isn't fixed yet


Added: trunk/data/qcsrc/common/mapinfo.qc
===================================================================
--- trunk/data/qcsrc/common/mapinfo.qc	                        (rev 0)
+++ trunk/data/qcsrc/common/mapinfo.qc	2007-11-15 10:30:27 UTC (rev 2951)
@@ -0,0 +1,272 @@
+// HUGE SET - stored in a string
+string HugeSetOfIntegers_empty()
+{
+	return "";
+}
+float HugeSetOfIntegers_get(string pArr, float i)
+{
+	return stof(substring(pArr, i * 4, 4));
+}
+float HugeSetOfIntegers_length(string pArr)
+{
+	return strlen(pArr) / 4;
+}
+string HugeSetOfIntegers_concat(string a1, string a2)
+{
+	return strcat(a1, a2);
+}
+string HugeSetOfIntegers_insert(string a1, float n, string a2)
+	// special concat function to build up large lists in less time by binary concatenation
+{
+	string s;
+	s = strcat("    ", ftos(n));
+	return strcat(a1, substring(s, strlen(s) - 4, 4), a2);
+}
+
+// generic string stuff
+float startsWith(string haystack, string needle)
+{
+	return substring(haystack, 0, strlen(needle)) == needle;
+}
+string extractRestOfLine(string haystack, string needle)
+{
+	if(startsWith(haystack, needle))
+		return substring(haystack, strlen(needle), strlen(haystack) - strlen(needle));
+	return string_null;
+}
+
+// GLOB HANDLING (for all BSP files)
+float _MapInfo_globopen;
+float _MapInfo_globcount; 
+float _MapInfo_globhandle;
+string _MapInfo_GlobItem(float i)
+{
+	string s;
+	s = search_getfilename(_MapInfo_globhandle, i);
+	return substring(s, 5, strlen(s) - 9); // without maps/ and .bsp
+}
+
+void MapInfo_Enumerate()
+{
+	if(_MapInfo_globopen)
+		search_end(_MapInfo_globhandle);
+	_MapInfo_globhandle = search_begin("maps/*.bsp", TRUE, TRUE);
+	_MapInfo_globcount = search_getsize(_MapInfo_globhandle);
+	_MapInfo_globopen = 1;
+}
+
+// filter the info by game type mask (updates MapInfo_count)
+string _MapInfo_filtered;
+string MapInfo_FilterGametype_Recursive(float pGametype, float pBegin, float pEnd)
+{
+	float m;
+	string l, r;
+
+	if(pBegin == pEnd)
+		return HugeSetOfIntegers_empty();
+
+	m = floor((pBegin + pEnd) / 2);
+
+	l = MapInfo_FilterGametype_Recursive(pGametype, pBegin, m);
+	if not(l)
+		return string_null; // BAIL OUT
+	if(MapInfo_Get_ByName(_MapInfo_GlobItem(m), 1) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame.
+		return string_null; // BAIL OUT
+	r = MapInfo_FilterGametype_Recursive(pGametype, m + 1, pEnd);
+	if not(r)
+		return string_null; // BAIL OUT
+
+	if(MapInfo_Map_supportedGametypes & pGametype)
+		return HugeSetOfIntegers_insert(l, m, r);
+	else
+		return HugeSetOfIntegers_concat(l, r);
+}
+float MapInfo_FilterGametype(float gametype)
+{
+	_MapInfo_filtered = MapInfo_FilterGametype_Recursive(gametype, 0, _MapInfo_globcount);
+	if(!_MapInfo_filtered)
+	{
+		dprint("Autogenerated a .mapinfo, bailing out to avoid loop counter\n");
+		return 0;
+	}
+	MapInfo_count = HugeSetOfIntegers_length(_MapInfo_filtered);
+	dprint("Filter ", ftos(gametype), " results in ", _MapInfo_filtered, "\n");
+	return 1;
+}
+
+// load info about the i-th map into the MapInfo_Map_* globals
+float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure
+
+float _MapInfo_Generate(string pFilename)
+{
+	string fn;
+	float fh;
+	string s, v;
+	vector o;
+	float inWorldspawn, l;
+
+	float spawns, diameter;
+	vector mapMins, mapMaxs;
+
+	fn = strcat("maps/", pFilename, ".ent");
+	fh = fopen(fn, FILE_READ);
+	if(fh < 0)
+	{
+		fn = strcat("maps/", pFilename, ".bsp");
+		fh = fopen(fn, FILE_READ);
+	}
+	if(fh < 0)
+		return 0;
+	dprint("Analyzing ", fn, " to generate initial mapinfo\n");
+
+	MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;      // DM always works
+	MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;       // Rune always works
+	MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;             // LMS always works
+
+	inWorldspawn = 2;
+
+	for(;;)
+	{
+		if not((s = fgets(fh)))
+			break;
+		if(inWorldspawn == 1)
+			if(startsWith(s, "}"))
+				inWorldspawn = 0;
+		if(inWorldspawn)
+		{
+			if(startsWith(s, "\"classname\" \"worldspawn\""))
+				inWorldspawn = 1;
+			else if((v = extractRestOfLine(s, "\"message\" \"")))
+			{
+				for(l = strlen(v) - 1; l > 0; --l)
+					if(substring(v, l, 1) == "\"")
+						break;
+				MapInfo_Map_title = substring(v, 0, l);
+			}
+		}
+		else
+		{
+			if((v = extractRestOfLine(s, "\"origin\" \"")))
+			{
+				for(l = strlen(v) - 1; l > 0; --l)
+					if(substring(v, l, 1) == "\"")
+						break;
+				o = stov(strcat("'", substring(v, 0, l), "'"));
+				mapMins_x = min(mapMins_x, o_x);
+				mapMins_y = min(mapMins_y, o_y);
+				mapMins_z = min(mapMins_z, o_z);
+				mapMaxs_x = max(mapMaxs_x, o_x);
+				mapMaxs_y = max(mapMaxs_y, o_y);
+				mapMaxs_z = max(mapMaxs_z, o_z);
+			}
+			else if((v = extractRestOfLine(s, "\"classname\" \"")))
+			{
+				if(startsWith(v, "dom_controlpoint\""))
+					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
+				else if(startsWith(v, "item_flag_team2\""))
+					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
+				else if(startsWith(v, "runematch_spawn_point\""))
+					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;
+				else if(startsWith(v, "target_assault_roundend\""))
+					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
+				else if(startsWith(v, "onslaught_generator\""))
+					MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
+				else if(startsWith(v, "info_player_team1\""))
+					++spawns;
+				else if(startsWith(v, "info_player_team2\""))
+					++spawns;
+				else if(startsWith(v, "info_player_deathmatch\""))
+					++spawns;
+				else if(startsWith(v, "info_player_start\""))
+					++spawns;
+			}
+		}
+	}
+	if(inWorldspawn)
+	{
+		print(strcat(fn, " ended still in worldspawn, BUG"));
+		return 0;
+	}
+	diameter = vlen(mapMaxs - mapMins);
+	if(spawns >= 8  && diameter > 2048)
+		MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
+	if(                diameter < 4096)
+		MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;
+	if(spawns >= 16 && diameter > 4096)
+		MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
+	fclose(fh);
+	dprint(fn, ": types = ", ftos(MapInfo_Map_supportedGametypes), " spawns ", ftos(spawns), " diameter ", ftos(diameter), "\n");
+	return 1;
+}
+
+// load info about a map by name into the MapInfo_Map_* globals
+float MapInfo_Get_ByName(string pFilename, float pAllowGenerate)
+{
+	string fn;
+	string s, t;
+	float fh;
+
+	// default all generic fields so they have "good" values in case something fails
+	MapInfo_Map_title = "Untitled1";
+	MapInfo_Map_description = "Bleh.";
+	MapInfo_Map_supportedGametypes = 0;
+
+	fn = strcat("maps/", pFilename, ".mapinfo");
+	fh = fopen(fn, FILE_READ);
+	if(fh < 0)
+	{
+		if(!pAllowGenerate)
+			return 0;
+		if(!_MapInfo_Generate(pFilename))
+			return 0;
+		fh = fopen(fn, FILE_WRITE);
+		fputs(fh, strcat("title ", MapInfo_Map_title, "\n"));
+		fputs(fh, strcat("description ", MapInfo_Map_description, "\n"));
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH)      fputs(fh, "type dm 30 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n"); // TODO count tdm_team entities
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION)      fputs(fh, "type dom 200 20 2\n"); // TODO count tdm_team entities
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTF)             fputs(fh, "type ctf 300 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RUNEMATCH)       fputs(fh, "type rune 200 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS)             fputs(fh, "type lms 9 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ARENA)           fputs(fh, "type arena 10 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEYHUNT)         fputs(fh, "type kh 1000 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT)         fputs(fh, "type as 20\n");
+		if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT)       fputs(fh, "type ons 20\n");
+		fclose(fh);
+		return 2;
+	}
+	for(;;)
+	{
+		if not((s = fgets(fh)))
+			break;
+		tokenize(s);
+		t = argv(0);
+		if(t == "title")
+			MapInfo_Map_title = substring(s, 6, strlen(s) - 6); // without "title"
+		else if(t == "description")
+			MapInfo_Map_description = substring(s, 12, strlen(s) - 12);
+		else if(t == "type")
+		{
+			t = argv(1);
+			if     (t == "dm")    MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;
+			else if(t == "tdm")   MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
+			else if(t == "dom")   MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
+			else if(t == "ctf")   MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
+			else if(t == "rune")  MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;
+			else if(t == "lms")   MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;
+			else if(t == "arena") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;
+			else if(t == "kh")    MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
+			else if(t == "as")    MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
+			else if(t == "ons")   MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
+			else
+				dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
+		}
+		else
+			dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
+	}
+	fclose(fh);
+	if(MapInfo_Map_supportedGametypes != 0)
+		return 1;
+	dprint("Map ", pFilename, " supports no game types, ignored\n");
+	return 0;
+}

Added: trunk/data/qcsrc/common/mapinfo.qh
===================================================================
--- trunk/data/qcsrc/common/mapinfo.qh	                        (rev 0)
+++ trunk/data/qcsrc/common/mapinfo.qh	2007-11-15 10:30:27 UTC (rev 2951)
@@ -0,0 +1,31 @@
+float MAPINFO_TYPE_DEATHMATCH		= 1;
+float MAPINFO_TYPE_TEAM_DEATHMATCH	= 2;
+float MAPINFO_TYPE_DOMINATION		= 4;
+float MAPINFO_TYPE_CTF				= 8;
+float MAPINFO_TYPE_RUNEMATCH		= 16;
+float MAPINFO_TYPE_LMS				= 32;
+float MAPINFO_TYPE_ARENA			= 64;
+float MAPINFO_TYPE_KEYHUNT			= 128;
+float MAPINFO_TYPE_ASSAULT			= 256;
+float MAPINFO_TYPE_ONSLAUGHT		= 512;
+float MAPINFO_TYPE_ALL              = 65535; // this has to include all above bits
+
+float MapInfo_count;
+
+// info about a map that MapInfo loads
+string MapInfo_Map_title;
+string MapInfo_Map_description;
+float MapInfo_Map_supportedGametypes;
+
+// load MapInfo_count; generate mapinfo for maps that miss them, and clear the
+// cache; you need to call MapInfo_FilterGametype afterwards!
+void MapInfo_Enumerate();
+
+// filter the info by game type mask (updates MapInfo_count)
+float MapInfo_FilterGametype(float gametype); // 1 on success, 0 on temporary failure (call it again next frame then)
+
+// load info about the i-th map into the MapInfo_Map_* globals
+float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure
+
+// load info about a map by name into the MapInfo_Map_* globals
+float MapInfo_Get_ByName(string s, float allowGenerate); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file

Modified: trunk/data/qcsrc/menu-div0test/mbuiltin.qh
===================================================================
--- trunk/data/qcsrc/menu-div0test/mbuiltin.qh	2007-11-14 17:24:07 UTC (rev 2950)
+++ trunk/data/qcsrc/menu-div0test/mbuiltin.qh	2007-11-15 10:30:27 UTC (rev 2951)
@@ -4,7 +4,6 @@
 // AK FIXME: Create perhaps a special builtin file for the common cmds
 
 //#define PROFILESTRZONE
-#define FIXEDFOPEN
 
 void 	checkextension(string ext) = #1;
 
@@ -88,11 +87,7 @@
 float	pow(float a, float b)  = #46;
 void	copyentity(entity src, entity dst)  = #47;
 
-#ifdef FIXEDFOPEN
-float 	_fopen( string filename, float mode ) = #48;
-#else
 float	fopen(string filename, float mode)  = #48;
-#endif
 void	fclose(float fhandle)  = #49;
 string	fgets(float fhandle)  = #50;
 void	fputs(float fhandle, string s)  = #51;
@@ -233,23 +228,3 @@
 float	gethostcacheindexforkey(string key) = #622;
 void	addwantedhostcachekey(string key) = #623;
 string	getextresponse(void) = #624;
-
-#ifdef FIXEDFOPEN
-float 	fopen( string filename, float mode ) =
-{
-	local float handle;
-	if( mode == FILE_READ ) {
-		return _fopen( filename, mode );
-	}
-
-	// check for data/
-	filename = strzone( filename );
-	if( substring( filename, 0, 5 ) != "data/" ) {
-		print( "menu: fopen: all output must go into data/!\n" );
-		return -1;
-	}
-	handle = _fopen( substring( filename, 5, 10000 ), mode );
-	strunzone( filename );
-	return handle;
-};
-#endif

Modified: trunk/data/qcsrc/menu-div0test/menu.qc
===================================================================
--- trunk/data/qcsrc/menu-div0test/menu.qc	2007-11-14 17:24:07 UTC (rev 2950)
+++ trunk/data/qcsrc/menu-div0test/menu.qc	2007-11-15 10:30:27 UTC (rev 2951)
@@ -11,12 +11,20 @@
 float menuPrevTime;
 float menuAlpha;
 float prevMenuAlpha;
+float menuLoadedMaplist;
 
 void SUB_Null() { };
 
 void() m_init =
 {
 	dprint_load();
+
+	menuLoadedMaplist = 0;
+	MapInfo_Enumerate();
+	if(!MapInfo_FilterGametype(MAPINFO_TYPE_ALL))
+		return;
+	menuLoadedMaplist = 1;
+
 	GameCommand_Init();
 
 	if(cvar("developer") == 42)
@@ -32,10 +40,15 @@
 	main.resizeNotify(main, draw_shift, draw_scale, draw_shift, draw_scale);
 	main.focused = 1;
 	menuShiftState = 0;
+
+	if(Menu_Active)
+		m_display(); // delayed menu display
 };
 
 void(float key, float ascii) m_keyup =
 {
+	if(!menuLoadedMaplist)
+		return;
 	if(!Menu_Active)
 		return;
 	draw_reset();
@@ -58,6 +71,8 @@
 
 void(float key, float ascii) m_keydown =
 {
+	if(!menuLoadedMaplist)
+		return;
 	if(!Menu_Active)
 		return;
 	if(keyGrabber)
@@ -96,6 +111,11 @@
 {
 	float t;
 	float realFrametime;
+	if(!menuLoadedMaplist)
+	{
+		m_init();
+		return;
+	}
 	t = gettime();
 	realFrametime = frametime = min(0.2, t - menuPrevTime);
 	menuPrevTime = t;
@@ -156,14 +176,16 @@
 void() m_display =
 {
 	Menu_Active = true;
+	setkeydest(KEY_MENU);
+	setmousetarget(MT_MENU);
 
+	if(!menuLoadedMaplist)
+		return;
+
 	if(mouseButtonsPressed)
 		main.mouseRelease(main, menuMousePos);
 	mouseButtonsPressed = 0;
 
-	setkeydest(KEY_MENU);
-	setmousetarget(MT_MENU);
-
 	main.focusEnter(main);
 	main.showNotify(main);
 };
@@ -174,6 +196,9 @@
 	setkeydest(KEY_GAME);
 	setmousetarget(MT_CLIENT);
 
+	if(!menuLoadedMaplist)
+		return;
+
 	main.focusLeave(main);
 	main.hideNotify(main);
 };
@@ -194,6 +219,8 @@
 void(string itemname) m_goto =
 {
 	entity e;
+	if(!menuLoadedMaplist)
+		return;
 	if(itemname == "") // this can be called by GameCommand
 	{
 		if(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))

Modified: trunk/data/qcsrc/menu-div0test/progs.src
===================================================================
--- trunk/data/qcsrc/menu-div0test/progs.src	2007-11-14 17:24:07 UTC (rev 2950)
+++ trunk/data/qcsrc/menu-div0test/progs.src	2007-11-15 10:30:27 UTC (rev 2951)
@@ -6,6 +6,9 @@
 oo/base.h
 
 ../common/util.qh
+
+../common/mapinfo.qh
+
 gamecommand.qh
 menu.qh
 draw.qh
@@ -27,3 +30,4 @@
 draw.qc
 nexuiz/util.qc
 
+../common/mapinfo.qc

Modified: trunk/data/qcsrc/server/progs.src
===================================================================
--- trunk/data/qcsrc/server/progs.src	2007-11-14 17:24:07 UTC (rev 2950)
+++ trunk/data/qcsrc/server/progs.src	2007-11-15 10:30:27 UTC (rev 2951)
@@ -12,6 +12,8 @@
 ../common/util.qh
 ../common/util.qc
 
+../common/mapinfo.qh
+
 ipban.qh
 
 keyhunt.qh
@@ -95,3 +97,5 @@
 assault.qc
 
 ipban.qc
+
+../common/mapinfo.qc




More information about the nexuiz-commits mailing list