Results 1 to 6 of 6

Thread: Smaller mappacks, less download

  1. #1
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts

    Smaller mappacks, less download

    This is a long post about how to create very small mappacks for your server. It relies on a modified version of the extension (...) and allows clients to only download the currently playing map instead of every map you have on the server.

    It is currently running on the JumpersHeaven server (/connect jh.killtube.org) without issues and as such I decided to release everything.

    Prerequisits:
    - A properly functioning brain capable of more advanced computer stuff.
    - Python 3.3
    - 7zip
    - Proper knowledge of codscript
    - Knowledge of libcod and compiling it
    - A few hours of your time
    - fs_game for your mod
    - Probably more

    First step is to install python 3.x if you dont already have it installed.

    Create a folder on C:/, I used JH2 (C:/JH2 is actually hardcoded in the python script, if you want to use some other folder you have to change the script)

    Create a folder "stock" in said folder, copy all (including localized_english) iwds from your cod2 directory to there and extract them in such a way the C:/JH2/stock/maps folder exists (extract on the spot, not to a subdir)

    Create a folder called "merged" in C:/JH2/ folder, extract all your mappacks to said folder

    Edit all your soundaliases files so every single map has a mapname.csv soundalias (there is NO check for this, you have to do it by hand)
    Edit all your soundaliases so they have a proper loadspec for every sound file (is technically not entirely needed, but the script is instructed to throw warnings if improper loadspecs are used which makes the output quite unreadable)

    Create a folder "add_models" and a folder "add_shaders" in C:/JH2/

    Look through the scripts of your maps to see if any additional xmodels or images are used.
    - If any additional xmodels are used, store their names in a mapname.txt files (do this per-map) in the add_models folder, layout (taken from jm_pier_2.txt):
    Code:
    xmodel/prop_lamp01_on
    xmodel/prop_lamp01
    xmodel/cod4_sunglasses
    xmodel/pier2_ancient_coin
    xmodel/caspi_desert_gold
    xmodel/de_v
    - Do the same for images the scripts use, store in same manner in add_shaders. Example:
    Code:
    pier2_credits_map_name
    objective
    pier2_manifest_paper
    cj_banner2
    unreal_jumperz_crew_rzl128x128
    lockpicking_cylinder_0
    lockpicking_cylinder_1
    lockpicking_cylinder_2
    lockpicking_cylinder_3
    lockpicking_cylinder_4
    lockpicking_cylinder_5
    lockpicking_cylinder_6
    lockpicking_cylinder_7
    lockpicking_cylinder_8
    lockpicking_cylinder_9
    lockpicking_pin
    lockpicking_background
    Note there is no folder here, just the material (!) name.

    Create a .bat file with the following contents in C:/JH2 as zip_packs.bat:
    Code:
    for /D %%d in (*.*) do 7z a -tzip "%%d.iwd" ".\%%d\*"
    This will later on pack the files into their .iwd packs.

    Now, make sure the create_packs2.py file is also in C:/JH2/. You can open a commandprompt there if you like, or just double-click the .py file, it should execute in a commandwindow. Note that doubleclicking does not allow you to see any errors with 7zip, so i advice you to open a commandprompt.

    The script now starts to do its magic. It does the following:
    - Find all .d3dbsp files in merged/maps/mp folder
    - Find all corresponding .csv files in merged/maps/mp to search loadingscreens, read them and add the found material files to a material list
    - Reads the d3dbsp file and finds all materials and all xmodels used within that certain map, then adds them to the material and xmodel lists respectively
    - Opens up all xmodel files (if not found in the stock files) and reads them to find all materials used by the xmodel. Note: this sometimes seems to fail, and the script will throw a "bad stuff happened for materialfind xmodelname mapname" error. Afaik this is not a real issue, as it seems the xmodel in question is not using any materials.
    - Reads xmodel to find xmodelsurf/xmodelparts used by it, then adding it to lists
    - Opens and reads the mapname.csv soundalias file (This is why you should name your soundalias file the same as the mapname), adds all sounds to a list
    - Goes through all material files and finds the corresponding .iwi files

    After these steps, it goes through all lists for that map and searches the stock folder for those files. If not found, it searches the merged folder for them. If still not found, it throws an error which you should try to fix.

    If a .iwi file is listed as not found, you SHOULD fix it for it MIGHT crash the server, which it surely will if the file in question is part of a loadscreen.

    Now that all maps have their own folder, the packing of the maps can begin. Pressing enter instructs 7zip to pack all separate maps into separate .iwd files (JumpersHeaven server has 200ish iwd files at the moment). These will be in the packs directory, automatically created by the script (and deleted once the script restarts, so be careful)

    After the script completes, you will have a couple of folders added to the C:/JH2/ folder, namely:

    - Packs
    - soundaliases
    - empty_files
    - filelist

    Packs:
    Contains all .iwd files of the individual maps, as well as the extracted counterparts in separate folders

    soundaliases
    Contains all read soundaliases files. This should be added to your mod iwd file, which should be zzz_something.iwd

    empty_files:
    Contains all files present in the packs, but as 0byte files, in order to fool cod2 into thinking the files are actually present. Should be packed into a 000something.iwd file as a client iwd in your fs_game

    filelist:
    Contains a lot of .txt files with 1 line of text each. This is the list of .iwd files needed to run a certain map and opens up doors to shared image packs for multiple maps, but it is not a requirement to change anything to this. Should be put in fs_game/scriptdata/mapfiles/ on your server and, if your server runs stock maps, accompanied by a couple of empty .txt files named mp_toujane.txt, mp_carentan etc.

    Now the packing of your maps is done it's time to start with the actual modding. You'll need a modified version of the libcod extension, with this present as function 602:
    Code:
    int gsc_system_command()
    {
    	char* cmd;
    	if (stackGetNumberOfParams() < 2) // function, command
    	{
    		printf_hide("scriptengine> ERROR: please specify atleast 2 arguments to gsc_system_command()\n");
    		return stackPushUndefined();
    	}
    	if (!stackGetParamString(1, &cmd))
    	{
    		printf_hide("scriptengine> ERROR: closer(): param \"cmd\"[1] has to be a string!\n");
    		return stackPushUndefined();
    	}
    	system(cmd);
    	return stackPushInt(0);
    }
    This should allow cod2 to directly access linux shell commands in order to dynamically link/unlink the .iwd files into your fs_game folder

    As this script totally breaks stock map functions, the /rcon map and /rcon devmap commands stop working and should be replaced by another function. As kung is currently looking into possible solutions for this I'm not gonna give you a function to replace these. If you require such a function, write it yourself.

    The voting however HAS been rewritten by me using the callback playercommand supplied by libcod. Your codecallback_playercommand in _callbacksetup should look like this:

    Code:
    CodeCallback_PlayerCommand(args) //depends on the extension, put it in callbacksetup.gsc
    {
    	args = fixChatArgs(args);
    	if(args[0] == "vote")
    	{
    		if(isdefined(args[1]) && args[1].size)
    		{
    			if(args[1] == "yes")
    			{
    				self vote(true);
    				return;
    			}
    			else if(args[1] == "no")
    			{
    				self vote(false);
    				return;
    			}
    		}
    	}
    	if(tolower(args[0]) == "callvote")
    	{
    		self callvote(args);
    		return;
    	}
    	std\utils::ClientCommand(self getEntityNumber());
    }
    in which you can put the vote commands in a separate .gsc file. These functions are:
    Code:
    init() //call on mapstart
    {
    	level.izno_votetime = 60 * 1000; 
    	level.izno_vote_cooldowntime = 60 * 1000;
    	level.nextmap = ??; //i dunno, i use my own mysql nextmap function here. Make sure to fill this out anyway, without it everything crashes and burns.
    }
    This should be read carefully as the level.nextmap is NOT set yet but SHOULD be set. I'm too lazy to find which cvars to read for this and am currently using a mysql function for nextmap.
    Furthermore, these functions should be included:
    Code:
    onvote(args)
    {
    	if(isdefined(args[1]) && args[1].size)
    	{
    		switch(tolower(args[1]))
    		{
    			case "map":
    			case "typemap":
    			{
    				if((args[1] == "map" && isdefined(args[2])) || (args[1] == "typemap" && isdefined(args[3])))
    				{
    					if(args[1] == "map")
    						num = 2;
    					else
    						num = 3;
    					mapname = args[num];
    					fid = openfile("mapfiles/" + mapname + ".txt", "read");
    					if(fid != -1)
    					{
    						closefile(fid);
    						self startmapvote(mapname);
    					}
    					else
    						self iprintln("No matching mapnames found");
    					return;
    				}
    			}
    			case "map_restart":
    			{
    				self startmap_restartvote();
    				return;
    			}
    			case "g_gametype":
    			{
    				self iprintlnbold("You are not allowed to change the gametype");
    				return;
    			}
    			case "map_rotate":
    			{
    				if(isdefined(level.nextmap))
    					self startmap_rotatevote();
    				return;
    			}
    			case "tempbanuser":
    			case "kick":
    			{
    				if(isdefined(args[2]) && args[2].size)
    					self startkickvote(args[2]);
    				return;
    			}
    			case "tempbanclient":
    			case "clientkick":
    			{
    				if(isdefined(args[2]) && args[2].size)
    					self startclientkickvote(args[2]);
    				return;
    			}
    		}
    	}
    }
    
    cancallvote()
    {
    	if(!getcvarint("g_allowvote"))
    	{
    		self iprintln("Voting is disabled");
    		return false;
    	}
    	if(isdefined(level.izno_vote) && level.izno_vote["endtime"] > gettime())
    	{
    		self iprintln("A vote is already in progress!");
    		return false;
    	}
    	else if(isdefined(level.izno_vote) && level.izno_vote["cooldown"] > gettime())
    	{
    		self iprintln("A vote was called recently");
    		self iprintln("Please wait before calling another one");
    		return false;
    	}
    	return true;
    }
    
    startmap_restartvote()
    {
    	if(!cancallvote())
    		return;
    	level.izno_vote = [];
    	level.izno_vote["veto"] = false;
    	level.izno_vote["type"] = "map_restart";
    	level.izno_vote["endtime"] = gettime() + level.izno_votetime;
    	level.izno_vote["in_favor"] = 1;
    	level.izno_vote["opposed"] = 0;
    	iprintlnbold("A vote has started");
    	iprintln(self.name + "^7 has called a vote to restart the map");
    	players = getentarray("player", "classname");
    	for(i = 0; i < players.size; i++)
    		players[i].izno_vote = undefined;
    	self.izno_vote = true;
    	level thread monitorvote();
    }
    startmap_rotatevote()
    {
    	if(!cancallvote())
    		return;
    	level.izno_vote = [];
    	level.izno_vote["veto"] = false;
    	level.izno_vote["type"] = "map_rotate";
    	level.izno_vote["map"] = level.nextmap;
    	level.izno_vote["endtime"] = gettime() + level.izno_votetime;
    	level.izno_vote["in_favor"] = 1;
    	level.izno_vote["opposed"] = 0;
    	iprintlnbold("A vote has started");
    	iprintln(self.name + "^7 has called a vote to start the next map");
    	players = getentarray("player", "classname");
    	for(i = 0; i < players.size; i++)
    		players[i].izno_vote = undefined;
    	self.izno_vote = true;
    	level thread monitorvote();
    }
    
    startclientkickvote(num)
    {
    	if(num != 0 && int(num) + "" != num)
    	{
    		self iprintln("Invalid client number");
    		return;
    	}
    	user = undefined;
    	players = getentarray("player", "classname");
    	for(i = 0; i < players.size; i++)
    	{
    		if(players[i] getentitynumber() == int(num))
    		{
    			user = players[i];
    			break;
    		}
    	}
    	if(isdefined(user) && self == user)
    		self iprintln("You cannot vote to kick yourself");
    	else if(isdefined(user))
    		self startkickplayervote(user);
    	else
    		self iprintln("No users found");
    }
    	
    startkickvote(name)
    {
    	if(!cancallvote())
    		return;
    	found = 0;
    	user = undefined;
    	players = getentarray("player", "classname");
    	for(i = 0; i < players.size; i++)
    	{
    		if(players[i].name == name)
    		{
    			found++;
    			user = players[i];
    		}
    	}
    	if(found == 1)
    	{
    		if(self == user)
    		{
    			self iprintln("You cannot vote to kick yourself");
    			return;
    		}
    		self startkickplayervote(user);
    	}
    	else if(!found)
    		self iprintln("No users found by the name " + name);
    	else
    		self iprintln("Multiple users found by the name " + name);
    }
    
    startkickplayervote(user)
    {
    	level.izno_vote = [];
    	level.izno_vote["veto"] = false;
    	level.izno_vote["type"] = "kick";
    	level.izno_vote["player"] = user;
    	level.izno_vote["endtime"] = gettime() + level.izno_votetime;
    	level.izno_vote["in_favor"] = 1;
    	level.izno_vote["opposed"] = 1;
    	iprintlnbold("A vote has started");
    	iprintln(self.name + "^7 has called a vote to kick " + user.name);
    	players = getentarray("player", "classname");
    	for(i = 0; i < players.size; i++)
    		players[i].izno_vote = undefined;
    	self.izno_vote = true;
    	user.izno_vote = false;
    	level thread monitorvote();
    }
    
    startmapvote(map)
    {
    	if(!cancallvote())
    		return;
    	level.izno_vote = [];
    	level.izno_vote["veto"] = false;
    	level.izno_vote["type"] = "map";
    	level.izno_vote["map"] = map;
    	level.izno_vote["endtime"] = gettime() + level.izno_votetime;
    	level.izno_vote["in_favor"] = 1;
    	level.izno_vote["opposed"] = 0;
    	iprintlnbold("A vote has started");
    	iprintln(self.name + "^7 has called a vote to change the map to " + map);
    	players = getentarray("player", "classname");
    	for(i = 0; i < players.size; i++)
    		players[i].izno_vote = undefined;
    	self.izno_vote = true;
    	level thread monitorvote();
    }
    
    vote(side)
    {
    	if(isdefined(self.izno_vote))
    	{
    		self iprintln("You have already voted");
    		return;
    	}
    	else if(isdefined(level.izno_vote) && level.izno_vote["endtime"] > gettime())
    	{
    		if(side)
    		{
    			self.izno_vote = true;
    			level.izno_vote["in_favor"]++;
    		}
    		else
    		{
    			self.izno_vote = false;
    			level.izno_vote["opposed"]++;
    		}
    	}
    	else
    		self iprintln("No vote in progress");
    }
    
    checkvotenotended()
    {
    	if(level.izno_vote["veto"])
    		return false;
    	players = getentarray("player", "classname");
    	in_favor = 0;
    	opposed = 0;
    	total = 0;
    	for(i = 0; i < players.size; i++)
    	{
    		if(isdefined(players[i].izno) && players[i].izno["afk_timer_vote"] > gettime())
    		{
    			if(isdefined(players[i].izno_vote))
    			{
    				if(players[i].izno_vote)
    					in_favor++;
    				else
    					opposed++;
    			}
    			total++;
    		}
    	}
    	level.izno_vote["in_favor"] = in_favor;
    	level.izno_vote["opposed"] = opposed;
    	level.izno_vote["total"] = total;
    	if(gettime() < level.izno_vote["endtime"] && level.izno_vote["in_favor"] <= level.izno_vote["total"] / 2 && level.izno_vote["opposed"] < level.izno_vote["total"] / 2)
    		return true;
    	return false;
    }
    
    monitorvote()
    {
    	players = getentarray("player", "classname");
    	remaining = (level.izno_vote["endtime"] - gettime()) / 1000;
    	level.izno_vote["cooldown"] = gettime() + level.izno_vote_cooldowntime;
    	oldtext_voted = "";
    	oldtext_notvoted = "";
    	text_voted = "";
    	text_notvoted = "";
    	while(checkvotenotended())
    	{
    		switch(level.izno_vote["type"])
    		{
    			case "map":
    			{
    				text_voted = "^3Vote(" + int(remaining) + "): Map: " + level.izno_vote["map"] + "^3\nYES: " + level.izno_vote["in_favor"] + " NO: " + level.izno_vote["opposed"];
    				text_notvoted = "^3Vote(" + int(remaining) + "): Map: " + level.izno_vote["map"] + "^3\nYES(F1): " + level.izno_vote["in_favor"] + " NO(F2): " + level.izno_vote["opposed"];
    				break;
    			}
    			case "map_restart":
    			{
    				text_voted =  "^3Vote(" + int(remaining) + "): Restart map \nYES: " + level.izno_vote["in_favor"] + " NO: " + level.izno_vote["opposed"];
    				text_notvoted = "^3Vote(" + int(remaining) + "): Restart map \nYES(F1): " + level.izno_vote["in_favor"] + " NO(F2): " + level.izno_vote["opposed"];
    				break;
    			}
    			case "map_rotate":
    			{
    				text_voted = "^3Vote(" + int(remaining) + "): Next map: " + level.izno_vote["map"] + "^3\nYES: " + level.izno_vote["in_favor"] + " NO: " + level.izno_vote["opposed"];
    				text_notvoted = "^3Vote(" + int(remaining) + "): Next map: " + level.izno_vote["map"] + "^3\nYES(F1): " + level.izno_vote["in_favor"] + " NO(F2): " + level.izno_vote["opposed"];
    				break;
    			}
    			case "kick":
    			{
    				if(isdefined(level.izno_vote["player"]))
    				{
    					text_voted = "^3Vote(" + int(remaining) + "): Kick: " + level.izno_vote["player"].name + "^3\nYES: " + level.izno_vote["in_favor"] + " NO: " + level.izno_vote["opposed"];
    					text_notvoted = "^3Vote(" + int(remaining) + "): Kick: " + level.izno_vote["player"].name + "^3\nYES(F1): " + level.izno_vote["in_favor"] + " NO(F2): " + level.izno_vote["opposed"];
    					break;
    				}
    				else
    					break;
    			}
    		}
    		if(oldtext_voted != text_voted || oldtext_notvoted != text_notvoted)
    		{
    			players = getentarray("player", "classname");
    			for(i = 0; i < players.size; i++)
    			{
    				if(isdefined(players[i].izno_vote))
    					players[i] setclientcvar("vote_alternative", text_voted);
    				else
    					players[i] setclientcvar("vote_alternative", text_notvoted);
    			}
    		}
    		remaining -= 0.05;
    		oldtext_voted = text_voted;
    		oldtext_notvoted = text_notvoted;
    		wait 0.05;
    	}
    	checkvotenotended();
    	players = getentarray("player", "classname");
    	for(i = 0; i < players.size; i++)
    		players[i] setclientcvar("vote_alternative", "");
    	if(level.izno_vote["type"] == "kick" && !isdefined(level.izno_vote["player"]))
    	{
    		iprintln("The player already left");
    		level.izno_vote = undefined;
    	}
    	if(level.izno_vote["veto"])
    		iprintlnbold("This vote was veto'd by an admin");
    	else if(level.izno_vote["in_favor"] > level.izno_vote["total"] / 2 || (level.izno_vote["in_favor"] > level.izno_vote["total"] / 4 && level.izno_vote["in_favor"] > level.izno_vote["opposed"] && level.izno_vote["total"] > 4))
    	{
    		switch(level.izno_vote["type"])
    		{
    			case "map":
    			{
    				iprintlnbold("The vote for map " + level.izno_vote["map"] + " passed");
    				level.nextmap = level.izno_vote["map"];
    				wait 3;
    				[[level.endgameconfirmed]](5);
    				break;
    			}
    			case "map_rotate":
    			{
    				iprintlnbold("The vote to start the next map (" + level.izno_vote["map"] + ")^7 passed");
    				level.nextmap = level.izno_vote["map"];
    				wait 3;
    				[[level.endgameconfirmed]](5);
    				break;
    			}
    			case "map_restart":
    			{
    				iprintlnbold("The vote for a map restart passed");
    				level.nextmap = getcvar("mapname");
    				wait 3;
    				[[level.endgameconfirmed]](0);
    				break;
    			}
    			case "kick":
    			{
    				iprintlnbold("The vote to kick " + level.izno_vote["player"].name + " ^7passed");
    				wait 3;
    				kick(level.izno_vote["player"] getentitynumber());
    				break;
    			}
    		}
    	}
    	else if(level.izno_vote["in_favor"] <= level.izno_vote["opposed"])
    		iprintln("Vote failed. Yes votes must exceed no votes");
    	else
    		iprintln("Vote failed. Not enough players voted");
    	level.izno_vote["endtime"] = gettime();
    }
    Furthermore, it needs a cvar in hud.menu:
    Code:
    	menuDef
    	{
    		name "vote_alternative"
    		fullScreen MENU_FALSE
    		visible MENU_TRUE
    		rect 0 0 0 0 HORIZONTAL_ALIGN_CENTER VERTICAL_ALIGN_CENTER
    		itemDef
    		{
    			name "text"
    			visible MENU_TRUE
    			rect 0 0 0 0
    			origin -310 -40
    			forecolor 1.0 1.0 1.0 1.0
    			dvar "vote_alternative"
    			textfont UI_FONT_NORMAL
    			textalign ITEM_ALIGN_LEFT
    			textscale 0.25
    			textaligny 0
    			decoration
    		}
    	}
    and a big modification to the endmap function in every gametype.gsc you have:
    Code:
    endMap(delay)
    {
    	if(!isdefined(delay))
    		delay = 10;
    	game["state"] = "intermission";
    	level notify("intermission");
    	if(delay != 0)
    	{
    		players = getentarray("player", "classname");
    		for(i = 0; i < players.size; i++)
    		{
    			players[i] closeMenu();
    			players[i] closeInGameMenu();
    			players[i] setClientCvar("cg_objectiveText", "Thanks for playing " + getcvar("mapname"));
    			players[i] spawnIntermission();
    		}
    		wait delay;
    	}
    	if(isdefined(level.nextmap))
    	{
    		do_nextmap = true;
    		to_link = [];
    		to_unlink = [];
    		fid = openfile("mapfiles/" + level.nextmap + ".txt", "read");
    		if(fid != -1)
    		{
    			argcount = freadln(fid);
    			while(argcount != -1)
    			{
    				str = "";
    				for(i = 0; i < argcount; i++)
    				{
    					if(i)
    						str += ",";
    					str += fgetarg(fid, i);
    				}
    				if(str.size)
    					to_link[to_link.size] = str;
    				argcount = freadln(fid);
    			}
    			closefile(fid);
    			fid = openfile("mapfiles/" + getcvar("mapname") + ".txt", "read");
    			if(fid != -1)
    			{
    				argcount = freadln(fid);
    				while(argcount != -1)
    				{
    					str = "";
    					for(i = 0; i < argcount; i++)
    					{
    						if(i)
    							str += ",";
    						str += fgetarg(fid, i);
    					}
    					if(str.size)
    						to_unlink[to_unlink.size] = str;
    					argcount = freadln(fid);
    				}
    				closefile(fid);
    			}
    			else
    				do_nextmap = false;
    		}
    		else
    			do_nextmap = false;
    		if(level.nextmap == getcvar("mapname") || !do_nextmap)
    			map_restart(false);
    		else
    		{
    			for(i = 0; i < to_unlink.size; i++)
    				closer(602, "unlink ~/" + getcvar("fs_game") + "/" + to_unlink[i] + ".iwd");
    			for(i = 0; i < to_link.size; i++)
    				closer(602, "link ~/" + getcvar("fs_game") + "/Library/" + to_link[i] + ".iwd ~/" + getcvar("fs_game") + "/" + to_link[i] + ".iwd");
    			map(level.nextmap);
    		}
    	}
    	else
    		exitLevel(false);
    	wait 10;
    	map_restart(false); //prevent server from accidentally hanging
    }
    If you add all above code at the proper locations, you now only need to move all your packed maps into fs_game/Library/ folder. The redirect however should have all files put into fs_game.

    All custom weapons and their material and image files should also be put in your mod iwd file.


    Advantages:
    - Smaller mappacks
    - Less stress on your download server
    - Clients can join faster
    - Voting is patched to include spectators into the vote
    - New maps can be added. I've found that updating the empty_files iwd file isnt enough: It should be renamed. I suggest you start with 000empty.iwd, use 001empty.iwd and so on for updates, forcing your clients to download the new file

    Drawbacks/known bugs:
    - map and devmap command dont work
    - vote is NOT visible for spectators that are spectating "free", but is visible for spectators that are spectating a player.
    - Script does NOT automatically clean up your fs_game after a servercrash. You should do this manually.
    - Your server should start a stock map at first, as no files are present in your fs_game at the start
    - It is a lot of work to do all this


    Disclaimer stuff:
    If any damage in whatever manner is caused by anything in this tutorial (like, ssd dying due to too much filecopy operations) I don't feel responsible, you dont have my address and cannot claim anything from me. No warranty on anything.

    License:
    Above code is provided as-is and is free to use for non-commercial use. Any support on creating the mappacks should be given free-of-charge except if done by me or after me telling the supporter it's ok to charge money for this. Help with the other scripts can be given at any fee you like.
    The scripts cannot be sold, nor can a modification of these scripts be sold. Any changes to these scripts should be given free-of-charge to anyone who asks for them

  2. The Following 5 Users Say Thank You to IzNoGoD For This Useful Post:

    buLLeT_ (10th March 2018),kung foo man (19th September 2013),Loveboy (22nd September 2013),randall (22nd September 2013),smect@ (22nd September 2013)

  3. #2
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    This is the python script that packs all your maps, should be named create_packs2.py to match the naming in this tutorial:
    Code:
    import os
    import shutil
    import subprocess
    import struct
    import csv
    
    def get_more_xmodels(filename):
        found = []
        if os.path.exists(filename):
            f = open(filename, "r")
            for line in f:
                if len(line) != 0:
                    found.append(line.strip('\n').strip('\r'))
        return found
    
    def get_more_materials(filename):
        found = []
        if os.path.exists(filename):
            f = open(filename, "r")
            for line in f:
                if len(line) != 0:
                    found.append(line.strip('\n').strip('\r'))
        return found
    
    
    def get_xmodels(filename):
        models = []
        f = open(filename, "rb")
        for line in f:
            if line.startswith(bytes(r'"model"', "utf-8")):
                #print(line)
                strs = ""
                start = False
                for chars in line:
                    if chr(chars) == " ":
                        start = True
                    elif chr(chars) == "\n":
                        break
                    elif start and chr(chars) == "*":
                        break;
                    elif start and chr(chars) != "\"":
                        strs += chr(chars)
                if len(strs):
                    if not strs in models:
                        models.append(strs)
        return models
    
    def getxmodelparts(filename, mapname):
        parts = []
        f = open(filename, "rb")
        res = f.read(1)
        pos = 1
        startpos = -1
        count_255 = 0
        while len(res) == 1:
            if res[0] == 0:
                count_255 += 1
                if count_255 == 4:
                    if startpos == -1:
                        startpos = pos
            else:
                count_255 = 0
            res = f.read(1)
            pos += 1
        f.close()
        f = open(filename, "rb")
        if startpos >= 1:
            f.read(startpos)
        else:
            f.close()
            print("bad stuff happened for part", filename, mapname)
            return parts
        char = f.read(1)
        pos = 1
        strs = ""
        while pos <= 32:
            if char == b"\x00":
                break;
            strs += char.decode("utf-8")
            char = f.read(1)
            pos += 1
        f.close()
        parts.append(strs)
        return parts
    
    def getmaterialsxmodel(filename, mapname):
        mats = []
        f = open(filename, "rb")
        res = f.read(1)
        pos = 1
        startpos = -1
        count_255 = 0
        while len(res) == 1:
            if res[0] == 255:
                count_255 += 1
                if count_255 == 4:
                    if startpos == -1:
                        startpos = pos
            else:
                count_255 = 0
            res = f.read(1)
            pos += 1
        f.close()
        f = open(filename, "rb")
        if startpos >= 0:
            f.read(startpos + 4)
        else:
            f.close()
            print("bad stuff happened for materialfind ", filename, mapname)
            return mats
        count = 0
        count += int(f.read(1)[0])
        count += int(f.read(1)[0]) * 256
        char = f.read(1)
        strs = ""
        while len(char) == 1 and len(mats) < count:
            if char == b"\x00":
                mats.append(strs)
                strs = ""
            else:
                strs += char.decode("utf-8")
            char = f.read(1)
        f.close()
        return mats
        
    
    def get_loadscreen(filename):
        mats = []
        if not os.path.exists(filename):
            return mats
        with open(filename) as csvfile:
            reader = csv.reader(csvfile, delimiter = ",")
            for row in reader:
                if len(row) < 2:
                    continue
                if len(row) == 2 and row[0].lower() == "levelbriefing":
                    mats.append(row[1])
                else:
                    print("bad stuff:", filename, row[0], row[1])
        return mats
                    
    
    def get_sounds(mapname):
        files = []
        if not os.path.exists(os.path.join(source, "soundaliases", mapname + ".csv")):
            return files
        with open(os.path.join(source, "soundaliases", mapname + ".csv")) as csvfile:
            file_index = -1
            loadspec_index = -1
            name_index = -1
            reader = csv.reader(csvfile, delimiter = ",")
            linenum = 0
            for row in reader:
                linenum += 1
                if len(row) < 3:
                    continue
                if row[0].startswith(r",") or row[0].startswith(r"#") or row[0].startswith(r"\"") or len(row[0]) == 0:
                    continue
                if file_index == -1 and "file" in row:
                    file_index = row.index("file")
                    if loadspec_index == -1 and "loadspec" in row:
                        loadspec_index = row.index("loadspec")
                    if name_index == -1 and "name" in row:
                        name_index = row.index("name")
                    continue
                if file_index == -1 or loadspec_index == -1:
                    continue
                if name_index != -1 and len(row) > name_index and row[name_index] == "null":
                    continue
                if len(row) <= loadspec_index or loadspec_index == -1:
                    print(mapname, "has loadspec out-of-bounds for", row[0])
                elif row[loadspec_index] != mapname:
                    print(mapname, "has invalid loadspec for", row[0])
                files.append(row[file_index])
        return files
    
    
    
    
    def get_materials(filename):
        f = open(filename, "rb")
        data = f.read(8)
        l = f.read(4)
        total_l = 0
        multi = 1
        for e in l:
            total_l += int(e) * multi
            multi *= 256
        o = f.read(4)
        total_o = 0
        multi = 1
        for e in o:
            total_o += int(e) * multi
            multi *= 256
        f.close()
        curloc = total_o
        mats = []
        while (curloc < total_o + total_l):
            f = open(filename, "rb");
            f.read(curloc)
            mat = ""
            loc = curloc
            while loc < curloc + 64:
                r = f.read(1)
                loc += 1
                if r == b"\x00":
                    break;
                mat += r.decode("utf-8")
            curloc += 72
            f.close()
            mats.append(mat)
        f.close()
        return mats
    
    def get_images(filename):
        f = open(filename, "rb")
        f.read(4)
        o = f.read(4)
        total_o = 0
        multi = 1
        for e in o:
            total_o += int(e) * multi
            multi *= 256
        f.close()
        mats = []
        f = open(filename, "rb")
        f.read(total_o)
        r = f.read(1)
        mat = ""
        while(len(r) > 0):
            if(r == b"\x00"):
                if len(mat) != 0:
                    mats.append(mat)
                mat = ""
            else:
                mat += r.decode("utf-8")
            r = f.read(1)
        f.close()
        return mats
    
    
    startdir = "C:/JH2"
    source = os.path.join(startdir, "merged")
    source_stock = os.path.join(startdir, "stock")
    dest_packs = os.path.join(startdir, "packs")
    dest_soundalias = os.path.join(startdir, "soundaliases")
    dest_empty = os.path.join(startdir, "empty_files")
    dest_filelist = os.path.join(startdir, "filelist")
    source_d3dbsp = os.path.join(source, "maps", "mp")
    source_add_models = os.path.join(startdir, "add_models")
    source_add_shaders = os.path.join(startdir, "add_shaders")
    
    if os.path.exists(dest_packs):
        shutil.rmtree(dest_packs)
    os.mkdir(dest_packs)
    
    
    if os.path.exists(dest_empty):
        shutil.rmtree(dest_empty)
    os.mkdir(dest_empty)
    
    if os.path.exists(dest_filelist):
        shutil.rmtree(dest_filelist)
    os.mkdir(dest_filelist)
    
    
    if os.path.exists(dest_soundalias):
        shutil.rmtree(dest_soundalias)
    os.mkdir(dest_soundalias)
    
    for r,d,f in os.walk(source_d3dbsp):
        for files in f:
            if not files.endswith(".d3dbsp"):
                continue
            mapname = os.path.splitext(files)[0]
            #print(mapname)
            if not os.path.exists(os.path.join(dest_packs, mapname)):
                os.mkdir(os.path.join(dest_packs, mapname))
            if not os.path.exists(os.path.join(dest_packs, mapname, "maps")):
                os.mkdir(os.path.join(dest_packs, mapname, "maps"))
            if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp")):
                os.mkdir(os.path.join(dest_packs, mapname, "maps", "mp"))
            if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".d3dbsp")):
                shutil.copy(os.path.join(source_d3dbsp, mapname + ".d3dbsp"), os.path.join(dest_packs, mapname, "maps", "mp"))
            if os.path.exists(os.path.join(source_d3dbsp, mapname + ".d3dprt")) and not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".d3dprt")):
                shutil.copy(os.path.join(source_d3dbsp, mapname + ".d3dprt"), os.path.join(dest_packs, mapname, "maps", "mp"))
            mats = get_materials(source_d3dbsp + "/" + files)
            models = get_xmodels(source_d3dbsp + "/" + files)
            models2 = get_more_xmodels(os.path.join(source_add_models, mapname + ".txt"))
            models = models + models2
            for m in models:
                if os.path.exists(os.path.join(source_stock, m)):
                    continue
                if not os.path.exists(os.path.join(source, m)):
                    print("Missing", m, "from map", mapname)
                    continue
                if not os.path.exists(os.path.join(dest_packs, mapname, m)):
                    if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, m))):
                        os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, m)))
                    shutil.copy(os.path.join(source, m), os.path.dirname(os.path.join(dest_packs, mapname, m)))
                mat_model = getmaterialsxmodel(source + "/" + m, mapname)
                mats = mats + mat_model
                xparts = getxmodelparts(source + "/" + m, mapname)
                #if len(xparts) == 0:
                #    print("No parts in", m)
                for p in xparts:
                    if os.path.exists(os.path.join(source_stock, "xmodelparts", p)):
                        #print("using stock for", p)
                        continue
                    if not os.path.exists(os.path.join(source, "xmodelparts", p)):
                        print("missing part", p, "for map", mapname)
                        continue
                    if not os.path.exists(os.path.join(dest_packs, mapname, "xmodelparts", p)):
                        if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p))):
                            os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p)))
                        shutil.copy(os.path.join(source, "xmodelparts", p), os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p)))
                        #print("copying xmodelpart", p)
                    #else:
                    #    print("too lazy to copy", p)
                for p in xparts:
                    if os.path.exists(os.path.join(source_stock, "xmodelsurfs", p)):
                        continue
                    if not os.path.exists(os.path.join(source, "xmodelsurfs", p)):
                        print("missing surf", p, "for map", mapname)
                        continue
                    if not os.path.exists(os.path.join(dest_packs, mapname, "xmodelsurfs", p)):
                        if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p))):
                            os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p)))
                        shutil.copy(os.path.join(source, "xmodelsurfs", p), os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p)))
            if os.path.exists(source_d3dbsp + "/" + mapname + ".csv"):
                mats_loadscreen = get_loadscreen(source_d3dbsp + "/" + mapname + ".csv")
                mats = mats + mats_loadscreen
                if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".csv")):
                    shutil.copy(os.path.join(source_d3dbsp + "/" + mapname + ".csv"), os.path.join(dest_packs, mapname, "maps", "mp"))
            mats2 = get_more_materials(os.path.join(source_add_shaders, mapname + ".txt"))
            mats = mats + mats2
            for mat in mats:
                if mat == "noshader":
                    continue
                if os.path.exists(os.path.join(source_stock, "materials", mat)):
                    continue
                if not os.path.exists(source + "/materials/" + mat):
                    print("Could not find material:", mat, "from map:", mapname)
                elif not os.path.exists(os.path.join(dest_packs, mapname, "materials", mat)):
                    if not os.path.exists(os.path.join(dest_packs, mapname, "materials")):
                        os.mkdir(os.path.join(dest_packs, mapname, "materials"))
                    shutil.copy(os.path.join(source, "materials", mat), os.path.join(dest_packs, mapname, "materials"))
                    imgs = get_images(os.path.join(dest_packs, mapname, "materials", mat))
                    for img in imgs:
                        if img.lower() == "colormap" or img.lower() == "detailmap" or img.lower() == "normalmap" or img.lower() == "detailscale" or img.lower() == "specularmap":
                            continue
                        if os.path.exists(os.path.join(source_stock, "images", img + ".iwi")):
                            continue
                        if not os.path.exists(os.path.join(source, "images", img + ".iwi")):
                            print("Could not find image:", img + ".iwi", "from map:", mapname)
                        elif not os.path.exists(os.path.join(dest_packs, mapname, "images", img + ".iwi")):
                            if not os.path.exists(os.path.join(dest_packs, mapname, "images")):
                                os.mkdir(os.path.join(dest_packs, mapname, "images"))
                            shutil.copy(os.path.join(source, "images", img + ".iwi"), os.path.join(dest_packs, mapname, "images"))
            sounds = get_sounds(mapname)
            if len(sounds) > 0:
                if not os.path.exists(os.path.join(dest_soundalias, mapname + ".csv")):
                    shutil.copy(os.path.join(source, "soundaliases", mapname + ".csv"), os.path.join(dest_soundalias, mapname + ".csv"))
                for sound in sounds:
                    if not os.path.exists(os.path.join(dest_packs, mapname, "sound", sound)):
                        if os.path.exists(os.path.join(source_stock, "sound", sound)):
                            continue
                        if os.path.exists(os.path.join(source, "sound", sound)):
                            d = os.path.dirname(os.path.join(dest_packs, mapname, "sound", sound))
                            if not os.path.exists(d):
                                os.makedirs(d)
                            if not os.path.exists(os.path.join(dest_packs, mapname, "sound", sound)):
                                shutil.copy(os.path.join(source, "sound", sound), os.path.join(dest_packs, mapname, "sound", sound))
                        else:
                            print("Could not find sound:", sound, "from map", mapname)
    
    for subdir in os.listdir(dest_packs):
        mapdir = os.path.join(dest_packs, subdir)
        for src_dir, dirs, files in os.walk(mapdir):
            dst_dir_empty = src_dir.replace(mapdir, dest_empty)
            if not os.path.exists(dst_dir_empty):
                os.mkdir(dst_dir_empty)
            for file_ in files:
                src_file = os.path.join(src_dir, file_)
                dst_empty_file = os.path.join(dst_dir_empty, file_)
                if not os.path.exists(dst_empty_file):
                    open(dst_empty_file, 'a').close()
    
    for subdir in os.listdir(dest_packs):
        for r,d,f in os.walk(os.path.join(dest_packs, subdir)):
            for files in f:
                if files.endswith(".d3dbsp"):
                    mapname = os.path.splitext(files)[0]
                    iwd = subdir
                    mapname = mapname.lower()
                    f = open(dest_filelist + "/" + mapname + ".txt", "a")
                    f.write(iwd + ",\n")
                    f.close()
    input("Press enter to start the packing of the maps")
    p = subprocess.Popen(os.path.join(startdir, "zip_packs.bat"), cwd=dest_packs)
    stdout, stderr = p.communicate()
    I know it is messy and shitty coded and could probably be done better/faster in c++, but the performance on an ssd is quite ok. If you want to improve this script, feel free to do so.
    Code might still throw warnings for ambient on normal channels. My mod uses a different way of playing ambient. If it bothers you, disable it.

    EDIT: ABOVE CODE IS DEPRECATED. LOOK FURTHER DOWN FOR UP_TO_DATE CODE
    Last edited by IzNoGoD; 31st October 2013 at 22:16.

  4. The Following User Says Thank You to IzNoGoD For This Useful Post:

    kung foo man (19th September 2013)

  5. #3
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    placeholder, if any such thing is possible on this site

  6. #4
    Lieutenant Loveboy's Avatar
    Join Date
    Nov 2012
    Posts
    546
    Thanks
    229
    Thanked 72 Times in 46 Posts
    Nice Tutorial! I have got a mappack with 70MB, it's too big and I found here a nice tutorial!

  7. #5
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    Update, added support for reading weapon files:

    Code:
    import os
    import shutil
    import subprocess
    import struct
    import csv
    
    def get_more_xmodels(filename):
        found = []
        if os.path.exists(filename):
            f = open(filename, "r")
            for line in f:
                if len(line) != 0:
                    found.append(line.strip('\n').strip('\r'))
        return found
    
    def get_more_materials(filename):
        found = []
        if os.path.exists(filename):
            f = open(filename, "r")
            for line in f:
                if len(line) != 0:
                    found.append(line.strip('\n').strip('\r'))
        return found
    
    def get_weapon_xmodels(filename):
        found = []
        models = []
        if os.path.exists(filename):
            f = open(filename, "r")
            for line in f:
                if len(line) != 0:
                    found.append(line.strip('\n').strip('\r'))
        for weap in found:
            if os.path.exists(os.path.join(source_stock, "weapons", "mp", weap)):
                continue
            if os.path.exists(os.path.join(source, "weapons", "mp", weap)):
                with open(os.path.join(source, "weapons", "mp", weap)) as weapfile:
                    reader = csv.reader(weapfile, delimiter = "\\")
                    num = 0
                    next_is_model = False
                    for row in reader:
                        for elem in row:
                            num += 1
                            if next_is_model:
                                next_is_model = False
                                if "xmodel/" in elem:
                                    models.append(elem)
                                else:
                                    models.append("xmodel/" + elem)
                            elif "model" in elem.lower() and not num%2:
                                next_is_model = True
                            else:
                                next_is_model = False
            else:
                print("weapon not found:", weap, "for map:", filename)
        return models
    
    def get_weapon_materials(filename):
        found = []
        materials = []
        if os.path.exists(filename):
            f = open(filename, "r")
            for line in f:
                if len(line) != 0:
                    found.append(line.strip('\n').strip('\r'))
        for weap in found:
            if os.path.exists(os.path.join(source_stock, "weapons", "mp", weap)):
                continue
            if os.path.exists(os.path.join(source, "weapons", "mp", weap)):
                with open(os.path.join(source, "weapons", "mp", weap)) as weapfile:
                    reader = csv.reader(weapfile, delimiter = "\\")
                    num = 0
                    next_is_material = False
                    for row in reader:
                        for elem in row:
                            num += 1
                            if next_is_material:
                                next_is_material = False
                                materials.append(elem)
                            elif "icon" in elem and not num%2:
                                next_is_material = True
                            else:
                                next_is_material = False
            else:
                print("weapon not found:", weap, "for map:", filename)
        return materials
    
    
    def get_xmodels(filename):
        models = []
        f = open(filename, "rb")
        for line in f:
            if line.startswith(bytes(r'"model"', "utf-8")):
                #print(line)
                strs = ""
                start = False
                for chars in line:
                    if chr(chars) == " ":
                        start = True
                    elif chr(chars) == "\n":
                        break
                    elif start and chr(chars) == "*":
                        break;
                    elif start and chr(chars) != "\"":
                        strs += chr(chars)
                if len(strs):
                    if not strs in models:
                        models.append(strs)
        return models
    
    def getxmodelparts(filename, mapname):
        parts = []
        f = open(filename, "rb")
        res = f.read(1)
        pos = 1
        startpos = -1
        count_255 = 0
        while len(res) == 1:
            if res[0] == 0:
                count_255 += 1
                if count_255 == 4:
                    if startpos == -1:
                        startpos = pos
            else:
                count_255 = 0
            res = f.read(1)
            pos += 1
        f.close()
        f = open(filename, "rb")
        if startpos >= 1:
            f.read(startpos)
        else:
            f.close()
            print("bad stuff happened for part", filename, mapname)
            return parts
        char = f.read(1)
        pos = 1
        strs = ""
        while pos <= 32:
            if char == b"\x00":
                break;
            strs += char.decode("utf-8")
            char = f.read(1)
            pos += 1
        f.close()
        parts.append(strs)
        return parts
    
    def getmaterialsxmodel(filename, mapname):
        mats = []
        f = open(filename, "rb")
        res = f.read(1)
        pos = 1
        startpos = -1
        count_255 = 0
        while len(res) == 1:
            if res[0] == 255:
                count_255 += 1
                if count_255 == 4:
                    if startpos == -1:
                        startpos = pos
            else:
                count_255 = 0
            res = f.read(1)
            pos += 1
        f.close()
        f = open(filename, "rb")
        if startpos >= 0:
            f.read(startpos + 4)
        else:
            f.close()
            print("bad stuff happened for materialfind ", filename, mapname)
            return mats
        count = 0
        count += int(f.read(1)[0])
        count += int(f.read(1)[0]) * 256
        char = f.read(1)
        strs = ""
        while len(char) == 1 and len(mats) < count:
            if char == b"\x00":
                mats.append(strs)
                strs = ""
            else:
                strs += char.decode("utf-8")
            char = f.read(1)
        f.close()
        return mats
        
    
    def get_loadscreen(filename):
        mats = []
        if not os.path.exists(filename):
            return mats
        with open(filename) as csvfile:
            reader = csv.reader(csvfile, delimiter = ",")
            for row in reader:
                if len(row) < 2:
                    continue
                if len(row) == 2 and row[0].lower() == "levelbriefing":
                    mats.append(row[1])
                else:
                    print("bad stuff:", filename, row[0], row[1])
        return mats
                    
    
    def get_sounds(mapname):
        files = []
        if not os.path.exists(os.path.join(source, "soundaliases", mapname + ".csv")):
            return files
        with open(os.path.join(source, "soundaliases", mapname + ".csv")) as csvfile:
            file_index = -1
            loadspec_index = -1
            name_index = -1
            reader = csv.reader(csvfile, delimiter = ",")
            linenum = 0
            for row in reader:
                linenum += 1
                if len(row) < 3:
                    continue
                if row[0].startswith(r",") or row[0].startswith(r"#") or row[0].startswith(r"\"") or len(row[0]) == 0:
                    continue
                if file_index == -1 and "file" in row:
                    file_index = row.index("file")
                    if loadspec_index == -1 and "loadspec" in row:
                        loadspec_index = row.index("loadspec")
                    if name_index == -1 and "name" in row:
                        name_index = row.index("name")
                    continue
                if file_index == -1 or loadspec_index == -1:
                    continue
                if name_index != -1 and len(row) > name_index and row[name_index] == "null":
                    continue
                if len(row) <= loadspec_index or loadspec_index == -1:
                    print(mapname, "has loadspec out-of-bounds for", row[0])
                elif row[loadspec_index] != mapname:
                    print(mapname, "has invalid loadspec for", row[0])
                files.append(row[file_index])
        return files
    
    
    
    
    def get_materials(filename):
        f = open(filename, "rb")
        data = f.read(8)
        l = f.read(4)
        total_l = 0
        multi = 1
        for e in l:
            total_l += int(e) * multi
            multi *= 256
        o = f.read(4)
        total_o = 0
        multi = 1
        for e in o:
            total_o += int(e) * multi
            multi *= 256
        f.close()
        curloc = total_o
        mats = []
        while (curloc < total_o + total_l):
            f = open(filename, "rb");
            f.read(curloc)
            mat = ""
            loc = curloc
            while loc < curloc + 64:
                r = f.read(1)
                loc += 1
                if r == b"\x00":
                    break;
                mat += r.decode("utf-8")
            curloc += 72
            f.close()
            mats.append(mat)
        f.close()
        return mats
    
    def get_images(filename):
        f = open(filename, "rb")
        f.read(4)
        o = f.read(4)
        total_o = 0
        multi = 1
        for e in o:
            total_o += int(e) * multi
            multi *= 256
        f.close()
        mats = []
        f = open(filename, "rb")
        f.read(total_o)
        r = f.read(1)
        mat = ""
        while(len(r) > 0):
            if(r == b"\x00"):
                if len(mat) != 0:
                    mats.append(mat)
                mat = ""
            else:
                mat += r.decode("utf-8")
            r = f.read(1)
        f.close()
        return mats
    
    
    startdir = "C:/JH2"
    source = os.path.join(startdir, "merged")
    source_stock = os.path.join(startdir, "stock")
    dest_packs = os.path.join(startdir, "packs")
    dest_soundalias = os.path.join(startdir, "soundaliases")
    dest_empty = os.path.join(startdir, "empty_files")
    dest_filelist = os.path.join(startdir, "filelist")
    source_d3dbsp = os.path.join(source, "maps", "mp")
    source_add_models = os.path.join(startdir, "add_models")
    source_add_shaders = os.path.join(startdir, "add_shaders")
    source_add_weapons = os.path.join(startdir, "add_weapons")
    
    if os.path.exists(dest_packs):
        shutil.rmtree(dest_packs)
    os.mkdir(dest_packs)
    
    
    if os.path.exists(dest_empty):
        shutil.rmtree(dest_empty)
    os.mkdir(dest_empty)
    
    if os.path.exists(dest_filelist):
        shutil.rmtree(dest_filelist)
    os.mkdir(dest_filelist)
    
    
    if os.path.exists(dest_soundalias):
        shutil.rmtree(dest_soundalias)
    os.mkdir(dest_soundalias)
    
    for r,d,f in os.walk(source_d3dbsp):
        for files in f:
            if not files.endswith(".d3dbsp"):
                continue
            mapname = os.path.splitext(files)[0]
            #print(mapname)
            if not os.path.exists(os.path.join(dest_packs, mapname)):
                os.mkdir(os.path.join(dest_packs, mapname))
            if not os.path.exists(os.path.join(dest_packs, mapname, "maps")):
                os.mkdir(os.path.join(dest_packs, mapname, "maps"))
            if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp")):
                os.mkdir(os.path.join(dest_packs, mapname, "maps", "mp"))
            if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".d3dbsp")):
                shutil.copy(os.path.join(source_d3dbsp, mapname + ".d3dbsp"), os.path.join(dest_packs, mapname, "maps", "mp"))
            if os.path.exists(os.path.join(source_d3dbsp, mapname + ".d3dprt")) and not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".d3dprt")):
                shutil.copy(os.path.join(source_d3dbsp, mapname + ".d3dprt"), os.path.join(dest_packs, mapname, "maps", "mp"))
            mats = get_materials(source_d3dbsp + "/" + files)
            models = get_xmodels(source_d3dbsp + "/" + files)
            models2 = get_more_xmodels(os.path.join(source_add_models, mapname + ".txt"))
            models3 = get_weapon_xmodels(os.path.join(source_add_weapons, mapname + ".txt"))
            models = models + models2 + models3
            for m in models:
                if os.path.exists(os.path.join(source_stock, m)):
                    continue
                if not os.path.exists(os.path.join(source, m)):
                    print("Missing", m, "from map", mapname)
                    continue
                if not os.path.exists(os.path.join(dest_packs, mapname, m)):
                    if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, m))):
                        os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, m)))
                    shutil.copy(os.path.join(source, m), os.path.dirname(os.path.join(dest_packs, mapname, m)))
                mat_model = getmaterialsxmodel(source + "/" + m, mapname)
                mats = mats + mat_model
                xparts = getxmodelparts(source + "/" + m, mapname)
                #if len(xparts) == 0:
                #    print("No parts in", m)
                for p in xparts:
                    if os.path.exists(os.path.join(source_stock, "xmodelparts", p)):
                        #print("using stock for", p)
                        continue
                    if not os.path.exists(os.path.join(source, "xmodelparts", p)):
                        print("missing part", p, "for map", mapname)
                        continue
                    if not os.path.exists(os.path.join(dest_packs, mapname, "xmodelparts", p)):
                        if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p))):
                            os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p)))
                        shutil.copy(os.path.join(source, "xmodelparts", p), os.path.dirname(os.path.join(dest_packs, mapname, "xmodelparts", p)))
                        #print("copying xmodelpart", p)
                    #else:
                    #    print("too lazy to copy", p)
                for p in xparts:
                    if os.path.exists(os.path.join(source_stock, "xmodelsurfs", p)):
                        continue
                    if not os.path.exists(os.path.join(source, "xmodelsurfs", p)):
                        print("missing surf", p, "for map", mapname)
                        continue
                    if not os.path.exists(os.path.join(dest_packs, mapname, "xmodelsurfs", p)):
                        if not os.path.exists(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p))):
                            os.makedirs(os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p)))
                        shutil.copy(os.path.join(source, "xmodelsurfs", p), os.path.dirname(os.path.join(dest_packs, mapname, "xmodelsurfs", p)))
            if os.path.exists(source_d3dbsp + "/" + mapname + ".csv"):
                mats_loadscreen = get_loadscreen(source_d3dbsp + "/" + mapname + ".csv")
                mats = mats + mats_loadscreen
                if not os.path.exists(os.path.join(dest_packs, mapname, "maps", "mp", mapname + ".csv")):
                    shutil.copy(os.path.join(source_d3dbsp + "/" + mapname + ".csv"), os.path.join(dest_packs, mapname, "maps", "mp"))
            mats2 = get_more_materials(os.path.join(source_add_shaders, mapname + ".txt"))
            mats3 = get_weapon_materials(os.path.join(source_add_weapons, mapname + ".txt"))
            mats = mats + mats2
            for mat in mats:
                if mat == "noshader":
                    continue
                if os.path.exists(os.path.join(source_stock, "materials", mat)):
                    continue
                if not os.path.exists(source + "/materials/" + mat):
                    print("Could not find material:", mat, "from map:", mapname)
                elif not os.path.exists(os.path.join(dest_packs, mapname, "materials", mat)):
                    if not os.path.exists(os.path.join(dest_packs, mapname, "materials")):
                        os.mkdir(os.path.join(dest_packs, mapname, "materials"))
                    shutil.copy(os.path.join(source, "materials", mat), os.path.join(dest_packs, mapname, "materials"))
                    imgs = get_images(os.path.join(dest_packs, mapname, "materials", mat))
                    for img in imgs:
                        if img.lower() == "colormap" or img.lower() == "detailmap" or img.lower() == "normalmap" or img.lower() == "detailscale" or img.lower() == "specularmap":
                            continue
                        if os.path.exists(os.path.join(source_stock, "images", img + ".iwi")):
                            continue
                        if not os.path.exists(os.path.join(source, "images", img + ".iwi")):
                            print("Could not find image:", img + ".iwi", "from map:", mapname)
                        elif not os.path.exists(os.path.join(dest_packs, mapname, "images", img + ".iwi")):
                            if not os.path.exists(os.path.join(dest_packs, mapname, "images")):
                                os.mkdir(os.path.join(dest_packs, mapname, "images"))
                            shutil.copy(os.path.join(source, "images", img + ".iwi"), os.path.join(dest_packs, mapname, "images"))
            sounds = get_sounds(mapname)
            if len(sounds) > 0:
                if not os.path.exists(os.path.join(dest_soundalias, mapname + ".csv")):
                    shutil.copy(os.path.join(source, "soundaliases", mapname + ".csv"), os.path.join(dest_soundalias, mapname + ".csv"))
                for sound in sounds:
                    if not os.path.exists(os.path.join(dest_packs, mapname, "sound", sound)):
                        if os.path.exists(os.path.join(source_stock, "sound", sound)):
                            continue
                        if os.path.exists(os.path.join(source, "sound", sound)):
                            d = os.path.dirname(os.path.join(dest_packs, mapname, "sound", sound))
                            if not os.path.exists(d):
                                os.makedirs(d)
                            if not os.path.exists(os.path.join(dest_packs, mapname, "sound", sound)):
                                shutil.copy(os.path.join(source, "sound", sound), os.path.join(dest_packs, mapname, "sound", sound))
                        else:
                            print("Could not find sound:", sound, "from map", mapname)
    
    for subdir in os.listdir(dest_packs):
        mapdir = os.path.join(dest_packs, subdir)
        for src_dir, dirs, files in os.walk(mapdir):
            dst_dir_empty = src_dir.replace(mapdir, dest_empty)
            if not os.path.exists(dst_dir_empty):
                os.mkdir(dst_dir_empty)
            for file_ in files:
                src_file = os.path.join(src_dir, file_)
                dst_empty_file = os.path.join(dst_dir_empty, file_)
                if not os.path.exists(dst_empty_file):
                    open(dst_empty_file, 'a').close()
    
    for subdir in os.listdir(dest_packs):
        for r,d,f in os.walk(os.path.join(dest_packs, subdir)):
            for files in f:
                if files.endswith(".d3dbsp"):
                    mapname = os.path.splitext(files)[0]
                    iwd = subdir
                    mapname = mapname.lower()
                    f = open(dest_filelist + "/" + mapname + ".txt", "a")
                    f.write(iwd + ",\n")
                    f.close()
    input("Press enter to start the packing of the maps")
    p = subprocess.Popen(os.path.join(startdir, "zip_packs.bat"), cwd=dest_packs)
    stdout, stderr = p.communicate()
    It works the same as the add_models/add_shaders, only now create your mapname.txt containing line-ending separated weaponnames in add_weapons folder

  8. #6
    Assadministrator IzNoGoD's Avatar
    Join Date
    Aug 2012
    Posts
    1,718
    Thanks
    17
    Thanked 1,068 Times in 674 Posts
    UPDATE

    Script now (_also_) does the following:

    - read all loadingscreens for all maps
    - convert them to dds using iwitodds.exe
    - resize them to be smaller (less size in mod) using nvidia dds tools
    - convert them back to iwi using dds2iwi.exe
    - rename to jh_mapimage_[mapname]
    - create a material file to go with that (had to reverse-engineer material files a bit for this)
    - list all maps in a .menu file, using the generated (small) iwi files as mapimages
    - dont modify stock loadingscreens (client doesnt need to download those anyway)
    - create an awesome mapvote menu

    Demo:
    http://classic.xfire.com/video/615d26/

    Script is private for now, if you want it, send me a PM.

  9. The Following User Says Thank You to IzNoGoD For This Useful Post:

    kung foo man (8th November 2013)

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •