Skip to content

Commit

Permalink
Move to Minecrift 1.8.1 method of two stage patching, client.md5 gene…
Browse files Browse the repository at this point in the history
…ration
  • Loading branch information
StellaArtois committed Mar 13, 2015
1 parent 9481b42 commit 51a3956
Show file tree
Hide file tree
Showing 39 changed files with 1,144 additions and 142 deletions.
19 changes: 11 additions & 8 deletions applychanges.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,32 +53,35 @@ def merge_tree(root_src_dir, root_dst_dir):
shutil.copy(src_file, dst_dir)


def applychanges(mcp_dir):
def applychanges(mcp_dir, patch_dir = "patches", backup = True, copyOriginal=True, mergeInNew=True ):
print("Applying Changes...")

mod_src_dir = os.path.join(mcp_dir, "src","minecraft")
mod_bak_dir = os.path.join(mcp_dir, "src","minecraft-bak")
org_src_dir = os.path.join(mcp_dir, "src",".minecraft_orig")

if os.path.exists(mod_src_dir):
if backup and os.path.exists(mod_src_dir):
print("Backing up src/minecraft to src/minecraft-bak")
shutil.rmtree( mod_bak_dir, True )
shutil.move( mod_src_dir, mod_bak_dir )
shutil.copytree( org_src_dir, mod_src_dir, ignore=lambda p,f: [".git"] )
if copyOriginal:
shutil.copytree( org_src_dir, mod_src_dir, ignore=lambda p,f: [".git"] )

#apply patches
apply_patches( mcp_dir, os.path.join( base_dir, "patches"), mod_src_dir )
apply_patches( mcp_dir, os.path.join( base_dir, patch_dir ), mod_src_dir )
#merge in the new classes
merge_tree( os.path.join( base_dir, "src" ), mod_src_dir )
if mergeInNew:
merge_tree( os.path.join( base_dir, "src" ), mod_src_dir )

if __name__ == '__main__':
parser = OptionParser()
parser.add_option('-m', '--mcp-dir', action='store', dest='mcp_dir', help='Path to MCP to use', default=None)
parser.add_option('-p', '--patch-dir', action='store', dest='patch_dir', help='Path to base patch dir', default='patches')
options, _ = parser.parse_args()

if not options.mcp_dir is None:
applychanges(os.path.abspath(options.mcp_dir))
applychanges(os.path.abspath(options.mcp_dir), options.patch_dir)
elif os.path.isfile(os.path.join('..', 'runtime', 'commands.py')):
applychanges(os.path.abspath('..'))
applychanges(os.path.abspath('..'), options.patch_dir)
else:
applychanges(os.path.abspath(mcp_version))
applychanges(os.path.abspath(mcp_version), options.patch_dir)
21 changes: 1 addition & 20 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from tempfile import mkstemp
from shutil import move
from os import remove, close
from install import download_deps, download_native, download_file, mkdir_p
from install import download_deps, download_native, download_file, mkdir_p, replacelineinfile
from minecriftversion import mc_version, of_file_name, of_json_name, minecrift_version_num, minecrift_build, of_file_extension, of_file_md5, mcp_version, forge_version

try:
Expand Down Expand Up @@ -137,25 +137,6 @@ def create_install(mcp_dir):
cwd=os.path.join(base_dir,"installer","launch4j"),
bufsize=-1).communicate()
os.unlink( "launch4j.xml" )

def replacelineinfile(file_path, pattern, subst):
#Create temp file
fh, abs_path = mkstemp()
new_file = open(abs_path,'w')
old_file = open(file_path)
for line in old_file:
if pattern in line:
new_file.write(subst)
else:
new_file.write(line)
#close temp file
new_file.close()
close(fh)
old_file.close()
#Remove original file
remove(file_path)
#Move new file
move(abs_path, file_path)

def main(mcp_dir):
print 'Using mcp dir: %s' % mcp_dir
Expand Down
3 changes: 1 addition & 2 deletions getchanges.bat
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
echo Building patches...
@echo off
python getchanges.py
python getchanges.py %*
35 changes: 28 additions & 7 deletions getchanges.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ def create_patch( target_dir, src_file, mod_file, label, patch_file ):
process = subprocess.Popen(cmd, cwd=target_dir, bufsize=-1, stdout=subprocess.PIPE)
stdout, stderr = process.communicate()
if stdout:
with open( patch_file, 'w') as out:
out.write( stdout )
with open( patch_file, 'wb') as out:
out.write( stdout.replace('\r\n','\n').replace('\r','\n') )

def main(mcp_dir):
def main(mcp_dir, patch_dir = "patches"):
new_src_dir = os.path.join( base_dir , "src" )
patch_base_dir = os.path.join( base_dir , "patches" )
patch_base_dir = os.path.join( base_dir , patch_dir )

try:
shutil.rmtree( new_src_dir )
Expand Down Expand Up @@ -81,16 +81,37 @@ def main(mcp_dir):
if os.path.exists( new_file ):
os.remove( new_file )
shutil.copy(mod_file, new_dir)

removeEmptyFolders(patch_base_dir)
removeEmptyFolders(new_src_dir)

def removeEmptyFolders(path):
if not os.path.isdir(path):
return

# remove empty subfolders
files = os.listdir(path)
if len(files):
for f in files:
fullpath = os.path.join(path, f)
if os.path.isdir(fullpath):
removeEmptyFolders(fullpath)

# if folder empty, delete it
files = os.listdir(path)
if len(files) == 0:
os.rmdir(path)


if __name__ == '__main__':
parser = OptionParser()
parser.add_option('-m', '--mcp-dir', action='store', dest='mcp_dir', help='Path to MCP to use', default=None)
parser.add_option('-p', '--patch-dir', action='store', dest='patch_dir', help='Patch dir base name to use', default='patches')
options, _ = parser.parse_args()

if not options.mcp_dir is None:
main(os.path.abspath(options.mcp_dir))
main(os.path.abspath(options.mcp_dir), options.patchdir)
elif os.path.isfile(os.path.join('..', 'runtime', 'commands.py')):
main(os.path.abspath('..'))
main(os.path.abspath('..'), options.patchdir)
else:
main(os.path.abspath(mcp_version))
main(os.path.abspath(mcp_version), options.patch_dir)
2 changes: 2 additions & 0 deletions getcompilechanges.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@echo off
python getchanges.py -p "mcppatches/patches"
2 changes: 2 additions & 0 deletions getcompilechanges.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
python getchanges.py -p "mcppatches/patches"
170 changes: 161 additions & 9 deletions install.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
import shutil, tempfile, json
import errno
import platform
import shutil
import time
from shutil import move
from tempfile import mkstemp
from os import remove, close
from minecriftversion import mc_version, of_file_name, of_json_name, minecrift_version_num, minecrift_build, of_file_extension, of_file_md5, mcp_version, mc_file_md5
from hashlib import md5 # pylint: disable-msg=E0611
from optparse import OptionParser
Expand All @@ -14,6 +19,9 @@

preferredarch = ''
nomerge = False
nopatch = False
clean = False
force = False

try:
WindowsError
Expand Down Expand Up @@ -96,7 +104,7 @@ def is_non_zero_file(fpath):
def download_deps( mcp_dir, download_mc ):

mcp_exists = True
if not os.path.exists(mcp_dir+"/runtime/commands.py "):
if not os.path.exists(mcp_dir+"/runtime/commands.py"):
mcp_exists = False
try:
mcp_zip_file = os.path.join( base_dir,mcp_version+".zip" )
Expand All @@ -116,15 +124,39 @@ def download_deps( mcp_dir, download_mc ):
print "No %s directory or zip file found. Please copy the %s.zip file into %s and re-run the command." % (mcp_dir, mcp_dir, base_dir)
exit(1)

# Patch mcp.cfg for additional mem
print("Patching mcp.cfg. Ignore \"FAILED\" hunks")
apply_patch( mcp_dir, os.path.join("mcppatches", "mcp.cfg.patch"), os.path.join(mcp_dir,"conf"))

client_md5 = os.path.join("mcppatches","client.md5")
target_client_md5 = os.path.join(mcp_dir,"temp","client.md5")
if not os.path.exists(target_client_md5):
mkdir_p( os.path.join(mcp_dir,"temp") )
print 'Updating client.md5: copying %s to %s' % (client_md5, target_client_md5)
shutil.copy(client_md5,target_client_md5)
# Patch mcp.cfg with minecraft jar md5
mcp_cfg_file = os.path.join(mcp_dir,"conf","mcp.cfg")
replacelineinfile( mcp_cfg_file, "MD5Client =", "MD5Client = %s\n" % mc_file_md5, True ); # Multiple 'MD5Client' entries - hack to get first one currently
#replacelineinfile( mcp_cfg_file, "MD5Server =", "MD5Server = %s\n" % mc_server_file_md5, True );

# patch joined.srg if necessary
mcp_joined_srg = os.path.join(mcp_dir,"conf","joined.srg")
patch_joined_srg = os.path.join(base_dir,"mcppatches","joined.srg")
if os.path.exists(patch_joined_srg):
print 'Updating joined.srg: copying %s to %s' % (patch_joined_srg, mcp_joined_srg)
shutil.copy(patch_joined_srg,mcp_joined_srg)

# Patch fffix.py
fffix_patch_path = os.path.join(base_dir, "mcppatches", "fffix.py.patch")
if os.path.exists(fffix_patch_path):
print("Patching fffix.py. Ignore \"FAILED\" hunks")
apply_patch( mcp_dir, os.path.join("mcppatches", "fffix.py.patch"), os.path.join(mcp_dir,"runtime","pylibs"))

# Patch Start.java with minecraft version
start_java_file = os.path.join(base_dir,"mcppatches","Start.java")
target_start_java_file = os.path.join(mcp_dir,"conf","patches","Start.java")
print 'Updating Start.java: copying %s to %s' % (start_java_file, target_start_java_file)
shutil.copy(start_java_file,target_start_java_file)
replacelineinfile( target_start_java_file, "args = concat(new String[] {\"--version\", \"mcp\"", " args = concat(new String[] {\"--version\", \"mcp\", \"--accessToken\", \"0\", \"--assetsDir\", \"assets\", \"--assetIndex\", \"%s\", \"--userProperties\", \"{}\"}, args);\n" % mc_version );

# Setup the appropriate mcp file versions
mcp_version_cfg = os.path.join(mcp_dir,"conf","version.cfg")
replacelineinfile( mcp_version_cfg, "ClientVersion =", "ClientVersion = %s\n" % mc_version );
replacelineinfile( mcp_version_cfg, "ServerVersion =", "ServerVersion = %s\n" % mc_version );

jars = os.path.join(mcp_dir,"jars")

Expand Down Expand Up @@ -348,6 +380,28 @@ def main(mcp_dir):
print 'Preferred architecture: %sbit - preferring %sbit native extraction (use -a 32 or -a 64 to change)' % (preferredarch, preferredarch)
if nomerge is True:
print 'NO Optifine merging'
if nopatch is True:
print 'SKIPPING Apply patches'

if clean == True:
print 'Cleaning...'
if force == False:
print ''
print 'WARNING:'
print 'The clean option will delete all folders created by MCP, including the'
print 'src folder which may contain changes you made to the code, along with any'
print 'saved worlds from the client or server.'
print 'Minecrift downloaded dependencies will also be removed and re-downloaded.'
print 'Patches will be left alone however.'
answer = raw_input('If you really want to clean up, enter "Yes" ')
if answer.lower() not in ['yes']:
print 'You have not entered "Yes", aborting the clean up process'
sys.exit(1)
print 'Cleaning mcp dir...'
reallyrmtree(mcp_dir)
print 'Cleaning lib dir...'
reallyrmtree(os.path.join(base_dir,'lib'))

print("\nDownloading dependencies...")
download_deps( mcp_dir, True )

Expand All @@ -358,7 +412,7 @@ def main(mcp_dir):
print ' Merging\n %s\n into\n %s' % (optifine, minecraft_jar)
zipmerge( minecraft_jar, optifine )
else:
print("Skipping Optifine merge...")
print("Skipping Optifine merge!")

print("Decompiling...")
src_dir = os.path.join(mcp_dir, "src","minecraft")
Expand All @@ -372,17 +426,111 @@ def main(mcp_dir):

os.chdir( base_dir )

# Create original decompile src dir
org_src_dir = os.path.join(mcp_dir, "src",".minecraft_orig")
if os.path.exists( org_src_dir ):
shutil.rmtree( org_src_dir, True )
shutil.copytree( src_dir, org_src_dir )

applychanges( mcp_dir )

if nopatch == False:
compile_error_patching_done = False

# Patch stage 1: apply only the patches needed to correct the
# optifine merge decompile errors
mcp_patch_dir = os.path.join( base_dir, "mcppatches", "patches" )
if os.path.exists( mcp_patch_dir ):
print("Patching Optifine merge decompile errors...")
applychanges( mcp_dir, patch_dir="mcppatches/patches", backup=False, copyOriginal=False, mergeInNew=False )
compile_error_patching_done = True

# Address problem files - copy over directly
problem_file_dir = os.path.join( base_dir, "mcppatches", "problemfiles" )
if os.path.exists( problem_file_dir ):
print("Addressing problem files...")
xp_problem_file = os.path.join(problem_file_dir, "xp.java")
shutil.copy( xp_problem_file, os.path.join( mcp_dir, "src", "minecraft", "net", "minecraft", "src", "xp.java" ) )
chunkrenderdispatcher_problem_file = os.path.join(problem_file_dir, "ChunkRenderDispatcher.java")
shutil.copy( chunkrenderdispatcher_problem_file, os.path.join( mcp_dir, "src", "minecraft", "net", "minecraft", "client", "renderer", "chunk", "ChunkRenderDispatcher.java" ) )
compile_error_patching_done = True

# Update the client md5
if compile_error_patching_done == True:
print("Updating client.md5...")
os.chdir(mcp_dir)
from runtime.updatemd5 import updatemd5
updatemd5( None, True, True, False )
os.chdir( base_dir )

# Now re-create the .minecraft_orig with the new buildable state
if os.path.exists( org_src_dir ):
shutil.rmtree( org_src_dir, True )
shutil.copytree( src_dir, org_src_dir )

# Patch stage 2: Now apply our main Minecrift patches, only
# changes needed for Minecrift functionality
print("Applying full Minecrift patches...")
applychanges( mcp_dir )
else:
print("Apply patches skipped!")

def reallyrmtree(path):
if not sys.platform.startswith('win'):
if os.path.exists(path):
shutil.rmtree(path)
else:
i = 0
try:
while os.stat(path) and i < 20:
shutil.rmtree(path, onerror=rmtree_onerror)
i += 1
except OSError:
pass

# raise OSError if the path still exists even after trying really hard
try:
os.stat(path)
except OSError:
pass
else:
raise OSError(errno.EPERM, "Failed to remove: '" + path + "'", path)

def rmtree_onerror(func, path, _):
if not os.access(path, os.W_OK):
os.chmod(path, stat.S_IWUSR)
time.sleep(0.5)
try:
func(path)
except OSError:
pass

def replacelineinfile(file_path, pattern, subst, firstmatchonly=False):
#Create temp file
fh, abs_path = mkstemp()
new_file = open(abs_path,'wb')
old_file = open(file_path,'rb')
hit = False
for line in old_file:
if pattern in line and not (firstmatchonly == True and hit == True):
new_file.write(subst)
hit = True
else:
new_file.write(line)
#close temp file
new_file.close()
close(fh)
old_file.close()
#Remove original file
remove(file_path)
#Move new file
move(abs_path, file_path)

if __name__ == '__main__':
parser = OptionParser()
parser.add_option('-o', '--no-optifine', dest='nomerge', default=False, action='store_true', help='If specified, no optifine merge will be carried out')
parser.add_option('-c', '--clean', dest='clean', default=False, action='store_true', help='Cleans the mcp dir, and REMOVES ALL SOURCE IN THE MCPxxx/SRC dir. Re-downloads dependencies')
parser.add_option('-f', '--force', dest='force', default=False, action='store_true', help='Forces any changes without prompts')
parser.add_option('-n', '--no-patch', dest='nopatch', default=False, action='store_true', help='If specified, no patches will be applied at the end of installation')
parser.add_option('-m', '--mcp-dir', action='store', dest='mcp_dir', help='Path to MCP to use', default=None)
parser.add_option('-a', '--architecture', action='store', dest='arch', help='Architecture to use (\'32\' or \'64\'); prefer 32 or 64bit dlls', default=None)
options, _ = parser.parse_args()
Expand All @@ -395,8 +543,12 @@ def main(mcp_dir):

if preferredarch is '':
preferredarch = osArch()


nomerge = options.nomerge
nopatch = options.nopatch
clean = options.clean
force = options.force

if not options.mcp_dir is None:
main(os.path.abspath(options.mcp_dir))
Expand Down
Loading

0 comments on commit 51a3956

Please sign in to comment.