4
4
import shutil , tempfile , json
5
5
import errno
6
6
import platform
7
+ import shutil
8
+ import time
9
+ from shutil import move
10
+ from tempfile import mkstemp
11
+ from os import remove , close
7
12
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
8
13
from hashlib import md5 # pylint: disable-msg=E0611
9
14
from optparse import OptionParser
14
19
15
20
preferredarch = ''
16
21
nomerge = False
22
+ nopatch = False
23
+ clean = False
24
+ force = False
17
25
18
26
try :
19
27
WindowsError
@@ -96,7 +104,7 @@ def is_non_zero_file(fpath):
96
104
def download_deps ( mcp_dir , download_mc ):
97
105
98
106
mcp_exists = True
99
- if not os .path .exists (mcp_dir + "/runtime/commands.py " ):
107
+ if not os .path .exists (mcp_dir + "/runtime/commands.py" ):
100
108
mcp_exists = False
101
109
try :
102
110
mcp_zip_file = os .path .join ( base_dir ,mcp_version + ".zip" )
@@ -116,15 +124,39 @@ def download_deps( mcp_dir, download_mc ):
116
124
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 )
117
125
exit (1 )
118
126
127
+ # Patch mcp.cfg for additional mem
119
128
print ("Patching mcp.cfg. Ignore \" FAILED\" hunks" )
120
129
apply_patch ( mcp_dir , os .path .join ("mcppatches" , "mcp.cfg.patch" ), os .path .join (mcp_dir ,"conf" ))
121
130
122
- client_md5 = os .path .join ("mcppatches" ,"client.md5" )
123
- target_client_md5 = os .path .join (mcp_dir ,"temp" ,"client.md5" )
124
- if not os .path .exists (target_client_md5 ):
125
- mkdir_p ( os .path .join (mcp_dir ,"temp" ) )
126
- print 'Updating client.md5: copying %s to %s' % (client_md5 , target_client_md5 )
127
- shutil .copy (client_md5 ,target_client_md5 )
131
+ # Patch mcp.cfg with minecraft jar md5
132
+ mcp_cfg_file = os .path .join (mcp_dir ,"conf" ,"mcp.cfg" )
133
+ replacelineinfile ( mcp_cfg_file , "MD5Client =" , "MD5Client = %s\n " % mc_file_md5 , True ); # Multiple 'MD5Client' entries - hack to get first one currently
134
+ #replacelineinfile( mcp_cfg_file, "MD5Server =", "MD5Server = %s\n" % mc_server_file_md5, True );
135
+
136
+ # patch joined.srg if necessary
137
+ mcp_joined_srg = os .path .join (mcp_dir ,"conf" ,"joined.srg" )
138
+ patch_joined_srg = os .path .join (base_dir ,"mcppatches" ,"joined.srg" )
139
+ if os .path .exists (patch_joined_srg ):
140
+ print 'Updating joined.srg: copying %s to %s' % (patch_joined_srg , mcp_joined_srg )
141
+ shutil .copy (patch_joined_srg ,mcp_joined_srg )
142
+
143
+ # Patch fffix.py
144
+ fffix_patch_path = os .path .join (base_dir , "mcppatches" , "fffix.py.patch" )
145
+ if os .path .exists (fffix_patch_path ):
146
+ print ("Patching fffix.py. Ignore \" FAILED\" hunks" )
147
+ apply_patch ( mcp_dir , os .path .join ("mcppatches" , "fffix.py.patch" ), os .path .join (mcp_dir ,"runtime" ,"pylibs" ))
148
+
149
+ # Patch Start.java with minecraft version
150
+ start_java_file = os .path .join (base_dir ,"mcppatches" ,"Start.java" )
151
+ target_start_java_file = os .path .join (mcp_dir ,"conf" ,"patches" ,"Start.java" )
152
+ print 'Updating Start.java: copying %s to %s' % (start_java_file , target_start_java_file )
153
+ shutil .copy (start_java_file ,target_start_java_file )
154
+ 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 );
155
+
156
+ # Setup the appropriate mcp file versions
157
+ mcp_version_cfg = os .path .join (mcp_dir ,"conf" ,"version.cfg" )
158
+ replacelineinfile ( mcp_version_cfg , "ClientVersion =" , "ClientVersion = %s\n " % mc_version );
159
+ replacelineinfile ( mcp_version_cfg , "ServerVersion =" , "ServerVersion = %s\n " % mc_version );
128
160
129
161
jars = os .path .join (mcp_dir ,"jars" )
130
162
@@ -348,6 +380,28 @@ def main(mcp_dir):
348
380
print 'Preferred architecture: %sbit - preferring %sbit native extraction (use -a 32 or -a 64 to change)' % (preferredarch , preferredarch )
349
381
if nomerge is True :
350
382
print 'NO Optifine merging'
383
+ if nopatch is True :
384
+ print 'SKIPPING Apply patches'
385
+
386
+ if clean == True :
387
+ print 'Cleaning...'
388
+ if force == False :
389
+ print ''
390
+ print 'WARNING:'
391
+ print 'The clean option will delete all folders created by MCP, including the'
392
+ print 'src folder which may contain changes you made to the code, along with any'
393
+ print 'saved worlds from the client or server.'
394
+ print 'Minecrift downloaded dependencies will also be removed and re-downloaded.'
395
+ print 'Patches will be left alone however.'
396
+ answer = raw_input ('If you really want to clean up, enter "Yes" ' )
397
+ if answer .lower () not in ['yes' ]:
398
+ print 'You have not entered "Yes", aborting the clean up process'
399
+ sys .exit (1 )
400
+ print 'Cleaning mcp dir...'
401
+ reallyrmtree (mcp_dir )
402
+ print 'Cleaning lib dir...'
403
+ reallyrmtree (os .path .join (base_dir ,'lib' ))
404
+
351
405
print ("\n Downloading dependencies..." )
352
406
download_deps ( mcp_dir , True )
353
407
@@ -358,7 +412,7 @@ def main(mcp_dir):
358
412
print ' Merging\n %s\n into\n %s' % (optifine , minecraft_jar )
359
413
zipmerge ( minecraft_jar , optifine )
360
414
else :
361
- print ("Skipping Optifine merge... " )
415
+ print ("Skipping Optifine merge! " )
362
416
363
417
print ("Decompiling..." )
364
418
src_dir = os .path .join (mcp_dir , "src" ,"minecraft" )
@@ -372,17 +426,111 @@ def main(mcp_dir):
372
426
373
427
os .chdir ( base_dir )
374
428
429
+ # Create original decompile src dir
375
430
org_src_dir = os .path .join (mcp_dir , "src" ,".minecraft_orig" )
376
431
if os .path .exists ( org_src_dir ):
377
432
shutil .rmtree ( org_src_dir , True )
378
433
shutil .copytree ( src_dir , org_src_dir )
379
434
380
- applychanges ( mcp_dir )
381
435
436
+ if nopatch == False :
437
+ compile_error_patching_done = False
438
+
439
+ # Patch stage 1: apply only the patches needed to correct the
440
+ # optifine merge decompile errors
441
+ mcp_patch_dir = os .path .join ( base_dir , "mcppatches" , "patches" )
442
+ if os .path .exists ( mcp_patch_dir ):
443
+ print ("Patching Optifine merge decompile errors..." )
444
+ applychanges ( mcp_dir , patch_dir = "mcppatches/patches" , backup = False , copyOriginal = False , mergeInNew = False )
445
+ compile_error_patching_done = True
446
+
447
+ # Address problem files - copy over directly
448
+ problem_file_dir = os .path .join ( base_dir , "mcppatches" , "problemfiles" )
449
+ if os .path .exists ( problem_file_dir ):
450
+ print ("Addressing problem files..." )
451
+ xp_problem_file = os .path .join (problem_file_dir , "xp.java" )
452
+ shutil .copy ( xp_problem_file , os .path .join ( mcp_dir , "src" , "minecraft" , "net" , "minecraft" , "src" , "xp.java" ) )
453
+ chunkrenderdispatcher_problem_file = os .path .join (problem_file_dir , "ChunkRenderDispatcher.java" )
454
+ shutil .copy ( chunkrenderdispatcher_problem_file , os .path .join ( mcp_dir , "src" , "minecraft" , "net" , "minecraft" , "client" , "renderer" , "chunk" , "ChunkRenderDispatcher.java" ) )
455
+ compile_error_patching_done = True
456
+
457
+ # Update the client md5
458
+ if compile_error_patching_done == True :
459
+ print ("Updating client.md5..." )
460
+ os .chdir (mcp_dir )
461
+ from runtime .updatemd5 import updatemd5
462
+ updatemd5 ( None , True , True , False )
463
+ os .chdir ( base_dir )
464
+
465
+ # Now re-create the .minecraft_orig with the new buildable state
466
+ if os .path .exists ( org_src_dir ):
467
+ shutil .rmtree ( org_src_dir , True )
468
+ shutil .copytree ( src_dir , org_src_dir )
469
+
470
+ # Patch stage 2: Now apply our main Minecrift patches, only
471
+ # changes needed for Minecrift functionality
472
+ print ("Applying full Minecrift patches..." )
473
+ applychanges ( mcp_dir )
474
+ else :
475
+ print ("Apply patches skipped!" )
476
+
477
+ def reallyrmtree (path ):
478
+ if not sys .platform .startswith ('win' ):
479
+ if os .path .exists (path ):
480
+ shutil .rmtree (path )
481
+ else :
482
+ i = 0
483
+ try :
484
+ while os .stat (path ) and i < 20 :
485
+ shutil .rmtree (path , onerror = rmtree_onerror )
486
+ i += 1
487
+ except OSError :
488
+ pass
489
+
490
+ # raise OSError if the path still exists even after trying really hard
491
+ try :
492
+ os .stat (path )
493
+ except OSError :
494
+ pass
495
+ else :
496
+ raise OSError (errno .EPERM , "Failed to remove: '" + path + "'" , path )
382
497
498
+ def rmtree_onerror (func , path , _ ):
499
+ if not os .access (path , os .W_OK ):
500
+ os .chmod (path , stat .S_IWUSR )
501
+ time .sleep (0.5 )
502
+ try :
503
+ func (path )
504
+ except OSError :
505
+ pass
506
+
507
+ def replacelineinfile (file_path , pattern , subst , firstmatchonly = False ):
508
+ #Create temp file
509
+ fh , abs_path = mkstemp ()
510
+ new_file = open (abs_path ,'wb' )
511
+ old_file = open (file_path ,'rb' )
512
+ hit = False
513
+ for line in old_file :
514
+ if pattern in line and not (firstmatchonly == True and hit == True ):
515
+ new_file .write (subst )
516
+ hit = True
517
+ else :
518
+ new_file .write (line )
519
+ #close temp file
520
+ new_file .close ()
521
+ close (fh )
522
+ old_file .close ()
523
+ #Remove original file
524
+ remove (file_path )
525
+ #Move new file
526
+ move (abs_path , file_path )
527
+
383
528
if __name__ == '__main__' :
384
529
parser = OptionParser ()
385
530
parser .add_option ('-o' , '--no-optifine' , dest = 'nomerge' , default = False , action = 'store_true' , help = 'If specified, no optifine merge will be carried out' )
531
+ 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' )
532
+ parser .add_option ('-f' , '--force' , dest = 'force' , default = False , action = 'store_true' , help = 'Forces any changes without prompts' )
533
+ 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' )
386
534
parser .add_option ('-m' , '--mcp-dir' , action = 'store' , dest = 'mcp_dir' , help = 'Path to MCP to use' , default = None )
387
535
parser .add_option ('-a' , '--architecture' , action = 'store' , dest = 'arch' , help = 'Architecture to use (\' 32\' or \' 64\' ); prefer 32 or 64bit dlls' , default = None )
388
536
options , _ = parser .parse_args ()
@@ -395,8 +543,12 @@ def main(mcp_dir):
395
543
396
544
if preferredarch is '' :
397
545
preferredarch = osArch ()
546
+
398
547
399
548
nomerge = options .nomerge
549
+ nopatch = options .nopatch
550
+ clean = options .clean
551
+ force = options .force
400
552
401
553
if not options .mcp_dir is None :
402
554
main (os .path .abspath (options .mcp_dir ))
0 commit comments