import argparse import itertools import numpy import pyopencl import sys import time def javaInt32(val): return ((val + (1 << 31)) % (1 << 32)) - (1 << 31) #javaInt32() def javaInt64(val): return ((val + (1 << 63)) % (1 << 64)) - (1 << 63) #javaInt64() def javaRandomInts(seed, size, count=1): seed = javaInt64(seed ^ 0x5DEECE66D) & ((1 << 48) - 1) vals = list() while len(vals) < count: bits = 0 vals.append(size) while javaInt32(bits - vals[-1] + size - 1) < 0: seed = javaInt64(seed * 0x5DEECE66D + 0xB) & ((1 << 48) - 1) bits = javaInt32(seed >> (48 - 31)) & ((1 << 31) - 1) vals[-1] = bits % size return vals #javaRandomInts() def javaRandomLongs(seed, count=1): seed = javaInt64(seed ^ 0x5DEECE66D) & ((1 << 48) - 1) vals = list() while len(vals) < count: seed = javaInt64(seed * 0x5DEECE66D + 0xB) & ((1 << 48) - 1) vals.append(javaInt32((seed >> (48 - 32)) & ((1 << (64 - 48 + 32)) - 1)) << 32) seed = javaInt64(seed * 0x5DEECE66D + 0xB) & ((1 << 48) - 1) vals[-1] = javaInt64(vals[-1] + javaInt32((seed >> (48 - 32)) & ((1 << (64 - 48 + 32)) - 1))) return vals #javaRandomLongs() def getSlimeChunkHash(worldseed, cx, cz): # alpha return javaInt64(worldseed + javaInt32(cx*cx*4987142) + javaInt32(cx*5947611) + javaInt64(cz*cz*4392871) + javaInt32(cz*389711)) #getSlimeChunkHash() def isSlimeChunkOK(hash, cx, cz, magic=987234911): # alpha return (javaRandomInts(javaInt64(hash ^ magic), 10)[0] == 0) #isSlimeChunkOK() def getVillageChunkHash(worldseed, cx, cz, magic=10387312): # beta return javaInt64(worldseed + cx//32*341873128712 + cz//32*132897987541 + magic) #getVillageChunkHash() def isVillageChunkOK(hash, cx, cz): # beta vals = javaRandomInts(hash, 24, 2) return ((vals[0] == (cx % 32)) and (vals[1] == (cz % 32))) #isVillageChunkOK() def getFeatureChunkHash(worldseed, cx, cz, magic=14357617): # 1.3.1 (Desert/Jungle Temple), 1.4.2 (Witch Hut), 1.9 (Igloo) return javaInt64(worldseed + cx//32*341873128712 + cz//32*132897987541 + magic) #getFeatureChunkHash() def isFeatureChunkOK(hash, cx, cz): # 1.3.1 (Desert/Jungle Temple), 1.4.2 (Witch Hut), 1.9 (Igloo) vals = javaRandomInts(hash, 24, 2) return ((vals[0] == (cx % 32)) and (vals[1] == (cz % 32))) #isFeatureChunkOK() def getMonumentChunkHash(worldseed, cx, cz, magic=10387313): # 1.8 return javaInt64(worldseed + cx//32*341873128712 + cz//32*132897987541 + magic) #getMonumentChunkHash() def isMonumentChunkOK(hash, cx, cz): # 1.8 vals = javaRandomInts(hash, 27, 4) return ((int(float(vals[0] + vals[1]) / 2.0) == (cx % 32)) and (int(float(vals[2] + vals[3]) / 2.0) == (cz % 32))) # must also have DeepOcean at center, and [Deep|Frozen] in radius 29 from center #isMonumentChunkOK() def getEndCityChunkHash(worldseed, cx, cz, magic=10387313): # 1.9 return javaInt64(worldseed + cx//20*341873128712 + cz//20*132897987541 + magic) #getEndCityChunkHash() def isEndCityChunkOK(hash, cx, cz): # 1.9 vals = javaRandomInts(hash, 9, 4) return ((int(float(vals[0] + vals[1]) / 2.0) == (cx % 20)) and (int(float(vals[2] + vals[3]) / 2.0) == (cz % 20))) # must also have Island height 60+ #isEndCityChunkOK() def getMansionChunkHash(worldseed, cx, cz, magic=10387319): # 1.11 return javaInt64(worldseed + cx//80*341873128712 + cz//80*132897987541 + magic) #getMansionChunkHash() def isMansionChunkOK(hash, cx, cz): # 1.11 vals = javaRandomInts(hash, 60, 4) return ((int(float(vals[0] + vals[1]) / 2.0) == (cx % 80)) and (int(float(vals[2] + vals[3]) / 2.0) == (cz % 80))) #isMansionChunkOK() def parseArgumentChunks(chunklist, blocklist): chunks = set() for chunk in chunklist: cx,cz = chunk.split(',') chunks.add( (int(cx),int(cz)) ) for block in blocklist: bx,bz = block.split(',') chunks.add( (int(bx)//16,int(bz)//16) ) return chunks #parseArgumentChunks() def timeToString(s): s,u = int(s),int(s*100)%100 m,s = s//60,s%60 h,m = m//60,m%60 d,h = h//24,h%24 str = "" if d: str += "%dd" % d if d or h: str += "%dh" % h if d or h or m: str += "%dm" % m str += "%d" % s if (not (d or h or m)) and (s < 10): str += ".%02d" % u str += "s" return str #timeToString() if __name__ == "__main__": # define usage and parse arguments parser = argparse.ArgumentParser(description="Seed Cracker") parser.add_argument('--version', action='version', version='1.0') parser.add_argument('--list-devices', action='store_true', help="list the OpenCL devices available on your computer" ) parser.add_argument('--devices', type=int, metavar='index', nargs='+', action='append', default=[], help="the particular OpenCL device(s) to use for seed scanning, identified by their index from --list-devices (default: automatic)" ) parser.add_argument('-w', '--world-seed', type=int, metavar='seed', nargs='+', action='append', default=[], help="world seed(s) for which to validate other provided feature locations" ) parser.add_argument('--ss', '--seed-slime', type=int, metavar='seed', nargs='?', default=10387313, const=None, help="the custom Spigot seed-slime, or leave blank to derive it with the world seed and slime chunk locations" ) parser.add_argument('--sv', '--seed-village', type=int, metavar='seed', nargs='?', default=10387312, const=None, help="the custom Spigot seed-village, or leave blank to derive it with the world seed and village locations" ) parser.add_argument('--sf', '--seed-feature', type=int, metavar='seed', nargs='?', default=14357617, const=None, help="the custom Spigot seed-feature, or leave blank to derive it with the world seed and scattered feature locations" ) parser.add_argument('--sm', '--seed-monument', type=int, metavar='seed', nargs='?', default=10387313, const=None, help="the custom Spigot seed-monument, or leave blank to derive it with the world seed and monument locations" ) parser.add_argument('--se', '--seed-endcity', type=int, metavar='seed', nargs='?', default=10387313, const=None, help="the custom Spigot seed-endcity, or leave blank to derive it with the world seed and end city locations" ) parser.add_argument('-s', '--sc', '--slime-chunk', type=str, metavar='cx,cz', nargs='+', action='append', default=[], help="comma-separated chunk coordinate(s) of slime spawning chunks", ) parser.add_argument('-S', '--sb', '--slime-block', type=str, metavar='bx,bz', nargs='+', action='append', default=[], help="comma-separated block coordinate(s) of slime spawning chunks", ) parser.add_argument('-v', '--vc', '--village-chunk', type=str, metavar='cx,cz', nargs='+', action='append', default=[], help="comma-separated chunk coordinate(s) of village wells", ) parser.add_argument('-V', '--vb', '--village-block', type=str, metavar='bx,bz', nargs='+', action='append', default=[], help="comma-separated block coordinate(s) of village wells", ) parser.add_argument('-f', '--fc', '--feature-chunk', type=str, metavar='cx,cz', nargs='+', action='append', default=[], help="comma-separated chunk coordinate(s) of scattered features (witch huts, desert/jungle temples, igloos)", ) parser.add_argument('-F', '--fb', '--feature-block', type=str, metavar='bx,bz', nargs='+', action='append', default=[], help="comma-separated block coordinate(s) of scattered features (witch huts, desert/jungle temples, igloos)", ) parser.add_argument('-m', '--mc', '--monument-chunk', type=str, metavar='cx,cz', nargs='+', action='append', default=[], help="comma-separated chunk coordinate(s) of ocean monuments", ) parser.add_argument('-M', '--mb', '--monument-block', type=str, metavar='bx,bz', nargs='+', action='append', default=[], help="comma-separated block coordinate(s) of ocean monuments", ) parser.add_argument('-e', '--ec', '--endcity-chunk', type=str, metavar='cx,cz', nargs='+', action='append', default=[], help="comma-separated chunk coordinate(s) of end cities", ) parser.add_argument('-E', '--eb', '--endcity-block', type=str, metavar='bx,bz', nargs='+', action='append', default=[], help="comma-separated block coordinate(s) of end cities", ) parser.add_argument('-a', '--ac', '--mansion-chunk', type=str, metavar='cx,cz', nargs='+', action='append', default=[], help="comma-separated chunk coordinate(s) of woodland mansions", ) parser.add_argument('-A', '--ab', '--mansion-block', type=str, metavar='bx,bz', nargs='+', action='append', default=[], help="comma-separated block coordinate(s) of woodland mansions", ) parser.add_argument('-r', '--resume', type=int, metavar='n', default=0, help="spot to resume a seed search that was stopped with Ctrl-C", ) if len(sys.argv) <= 1: parser.print_usage() print("-h for more information") sys.exit(2) args = parser.parse_args() worldseeds = sorted(set(itertools.chain(*args.world_seed))) slimechunks = parseArgumentChunks(itertools.chain(*args.sc), itertools.chain(*args.sb)) villagechunks = parseArgumentChunks(itertools.chain(*args.vc), itertools.chain(*args.vb)) featurechunks = parseArgumentChunks(itertools.chain(*args.fc), itertools.chain(*args.fb)) monumentchunks = parseArgumentChunks(itertools.chain(*args.mc), itertools.chain(*args.mb)) endcitychunks = parseArgumentChunks(itertools.chain(*args.ec), itertools.chain(*args.eb)) mansionchunks = parseArgumentChunks(itertools.chain(*args.ac), itertools.chain(*args.ab)) # list OpenCL devices? if args.list_devices: d = 0 print("Listing available OpenCL devices ...") for platform in pyopencl.get_platforms(): for device in platform.get_devices(): d += 1 print(" #%d: %s on %s (%s)" % (d, platform.name, device.name, platform.version)) print("... OK: %d devices" % (d,)) sys.exit(0) #if list_devices # validate provided seeds? if len(worldseeds) > 0 and args.sv != None and args.sf != None and args.sm != None and args.ss != None: print("Validating provided world seeds and locations using Python logic ...") for s in worldseeds: out = "" if slimechunks: sNum = 0 for cx,cz in slimechunks: if isSlimeChunkOK(getSlimeChunkHash(s, cx, cz), cx, cz, args.ss): sNum += 1 out += "%s%d/%d slime chunks" % ((", " if out else ""),sNum,len(slimechunks)) if villagechunks: vNum = 0 for cx,cz in villagechunks: if isVillageChunkOK(getVillageChunkHash(s, cx, cz, args.sv), cx, cz): vNum += 1 out += "%s%d/%d villages" % ((", " if out else ""),vNum,len(villagechunks)) if featurechunks: fNum = 0 for cx,cz in featurechunks: if isFeatureChunkOK(getFeatureChunkHash(s, cx, cz, args.sf), cx, cz): fNum += 1 out += "%s%d/%d features" % ((", " if out else ""),fNum,len(featurechunks)) if monumentchunks: mNum = 0 for cx,cz in monumentchunks: if isMonumentChunkOK(getMonumentChunkHash(s, cx, cz, args.sm), cx, cz): mNum += 1 out += "%s%d/%d monuments" % ((", " if out else ""),mNum,len(monumentchunks)) if endcitychunks: fNum = 0 for cx,cz in endcitychunks: if isEndCityChunkOK(getEndCityChunkHash(s, cx, cz, args.sm), cx, cz): fNum += 1 out += "%s%d/%d end cities" % ((", " if out else ""),fNum,len(endcitychunks)) if mansionchunks: mNum = 0 for cx,cz in mansionchunks: if isMansionChunkOK(getMansionChunkHash(s, cx, cz), cx, cz): mNum += 1 out += "%s%d/%d mansions" % ((", " if out else ""),mNum,len(mansionchunks)) print(" %d matches %s" % (s,out)) print("... OK") #if worldseeds # dynamically generate an OpenCL kernel function kernel = """ __kernel void test_seeds( const long seedbase, const char asTimestamp, __global volatile long *hits, __global volatile uint *count ) { long seed = seedbase + get_global_id(0); long rand; int bits, val, val2; /* DISABLED timestamp-derived scan; newer JDK uses System.nanoTime() and a secondary Uniquifier() which depends how many instances of Random() have ever been created if (asTimestamp != 0) { rand = (seed ^ 0x5deece66dL) & ((1L << 48) - 1); rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); seed = (long)( (int)( (rand >> (48 - 32)) & ((1L << (64 - 48 + 32)) - 1) ) ) << 32; rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); seed = seed + (int)( (rand >> (48 - 32)) & ((1L << (64 - 48 + 32)) - 1) ); } */ """ + "\n".join((""" rand = ((seed + %dL) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; """ % (getVillageChunkHash(0,cx,cz,args.sv),cx%32,cz%32)) for cx,cz in villagechunks if (args.sv != None)) + """ """ + "\n".join((""" rand = ((seed + %dL) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; """ % (getFeatureChunkHash(0,cx,cz,args.sf),cx%32,cz%32)) for cx,cz in featurechunks if (args.sf != None)) + """ """ + "\n".join((""" rand = ((seed + %dL) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 27; } while (bits - val + (27 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 27; } while (bits - val2 + (27 - 1) < 0); if ((val + val2) / 2 != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 27; } while (bits - val + (27 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 27; } while (bits - val2 + (27 - 1) < 0); if ((val + val2) / 2 != %d) return; """ % (getMonumentChunkHash(0,cx,cz,args.sm),cx%32,cz%32)) for cx,cz in monumentchunks if (args.sm != None)) + """ """ + "\n".join((""" rand = ((seed + %dL) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 9; } while (bits - val + (9 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 9; } while (bits - val2 + (9 - 1) < 0); if ((val + val2) / 2 != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 9; } while (bits - val + (9 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 9; } while (bits - val2 + (9 - 1) < 0); if ((val + val2) / 2 != %d) return; """ % (getEndCityChunkHash(0,cx,cz),cx%20,cz%20)) for cx,cz in endcitychunks) + """ """ + "\n".join((""" rand = ((seed + %dL) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 60; } while (bits - val + (60 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 60; } while (bits - val2 + (60 - 1) < 0); if ((val + val2) / 2 != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 60; } while (bits - val + (60 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 60; } while (bits - val2 + (60 - 1) < 0); if ((val + val2) / 2 != %d) return; """ % (getMansionChunkHash(0,cx,cz),cx%80,cz%80)) for cx,cz in mansionchunks) + """ """ + "\n".join((""" rand = ((seed + %dL) ^ %dL ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 10; } while (bits - val + (10 - 1) < 0); if (val != 0) return; """ % (getSlimeChunkHash(0,cx,cz),args.ss)) for cx,cz in slimechunks if (args.ss != None)) + """ val = (int) atomic_inc(count); if ((val >= 0 && val < 16)) { hits[val] = seed; } else { atomic_xchg(count, 16); } } """ # add kernel functions for slime/village/feature/monument seed search, if possible if len(worldseeds) == 1: #TODO slimes if args.sv == None: kernel += (""" __kernel void test_villageseeds( const long magicbase, const char asTimestamp, __global volatile long *hits, __global volatile uint *count ) { long magic = magicbase + get_global_id(0); long rand; int bits, val, val2; """) + "\n".join((""" rand = ((%dL + magic) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; """ % (getVillageChunkHash(worldseeds[0],cx,cz,0),cx%32,cz%32)) for cx,cz in villagechunks) + (""" val = (int) atomic_inc(count); if ((val >= 0 && val < 16)) { hits[val] = magic; } else { atomic_xchg(count, 16); } } """) #if no sv if args.sf == None: kernel += (""" __kernel void test_featureseeds( const long magicbase, const char asTimestamp, __global volatile long *hits, __global volatile uint *count ) { long magic = magicbase + get_global_id(0); long rand; int bits, val, val2; """) + "\n".join((""" rand = ((%dL + magic) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 24; } while (bits - val + (24 - 1) < 0); if (val != %d) return; """ % (getFeatureChunkHash(worldseeds[0],cx,cz,0),cx%32,cz%32)) for cx,cz in featurechunks) + (""" val = (int) atomic_inc(count); if ((val >= 0 && val < 16)) { hits[val] = magic; } else { atomic_xchg(count, 16); } } """) #if no sf if args.sm == None: kernel += (""" __kernel void test_monumentseeds( const long magicbase, const char asTimestamp, __global volatile long *hits, __global volatile uint *count ) { long magic = magicbase + get_global_id(0); long rand; int bits, val, val2; """) + "\n".join((""" rand = ((%dL + magic) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 27; } while (bits - val + (27 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 27; } while (bits - val2 + (27 - 1) < 0); if ((val + val2) / 2 != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 27; } while (bits - val + (27 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 27; } while (bits - val2 + (27 - 1) < 0); if ((val + val2) / 2 != %d) return; """ % (getMonumentChunkHash(worldseeds[0],cx,cz,0),cx%32,cz%32)) for cx,cz in monumentchunks) + (""" val = (int) atomic_inc(count); if ((val >= 0 && val < 16)) { hits[val] = magic; } else { atomic_xchg(count, 16); } } """) #if no sm if args.se == None: kernel += (""" __kernel void test_endcityseeds( const long magicbase, const char asTimestamp, __global volatile long *hits, __global volatile uint *count ) { long magic = magicbase + get_global_id(0); long rand; int bits, val, val2; """) + "\n".join((""" rand = ((%dL + magic) ^ 0x5deece66dL) & ((1L << 48) - 1); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 9; } while (bits - val + (9 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 9; } while (bits - val2 + (9 - 1) < 0); if ((val + val2) / 2 != %d) return; do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val = bits %% 9; } while (bits - val + (9 - 1) < 0); do { rand = (rand * 0x5deece66dL + 0xbL) & ((1L << 48) - 1); bits = (int)((rand >> (48 - 31)) & ((1L << 31) - 1)); val2 = bits %% 9; } while (bits - val2 + (9 - 1) < 0); if ((val + val2) / 2 != %d) return; """ % (getEndCityChunkHash(worldseeds[0],cx,cz,0),cx%20,cz%20)) for cx,cz in endcitychunks) + (""" val = (int) atomic_inc(count); if ((val >= 0 && val < 16)) { hits[val] = magic; } else { atomic_xchg(count, 16); } } """) #if no se #if 1 worldseed deviceIDs = set(itertools.chain(*args.devices)) mydevices = list() if deviceIDs: d = 0 for platform in pyopencl.get_platforms(): for device in platform.get_devices(): d += 1 if d in deviceIDs: mydevices.append(device) if not mydevices: sys.exit("ERROR: The specified OpenCL device(s) were not found; refer to --list-devices") context = pyopencl.Context(devices=mydevices) else: context = pyopencl.create_some_context() queue = pyopencl.CommandQueue(context) program = pyopencl.Program(context, kernel).build() hits = numpy.zeros(16, dtype=numpy.int64) count = numpy.zeros(1, dtype=numpy.uint32) hitsBuf = pyopencl.Buffer(context, pyopencl.mem_flags.WRITE_ONLY | pyopencl.mem_flags.COPY_HOST_PTR, hostbuf=hits) countBuf = pyopencl.Buffer(context, pyopencl.mem_flags.READ_WRITE | pyopencl.mem_flags.COPY_HOST_PTR, hostbuf=count) if (args.ss == None) or (args.sv == None) or (args.sf == None) or (args.sm == None) or (args.se == None): # TODO slimes if args.sv == None: if len(worldseeds) != 1: sys.exit("ERROR: village seed search requires exactly one world seed") if not villagechunks: sys.exit("ERROR: village seed search requires at least one village location") print("Searching for 32-bit village seed with world seed %d ..." % (worldseeds[0],)) results = set() t0 = time.time() for magicbase in [0, ((1<<17)-1)]: event = program.test_villageseeds(queue, (1<<31,), None, numpy.int64(magicbase*(1<<31)), numpy.int8(0), hitsBuf, countBuf) event.wait() pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) found = set(hits[h] for h in range(min(16,count[0]))) for s in sorted(found): print(" Match: %d" % (s,)) results.update(found) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) t1 = time.time() print("... OK: Search completed in %s, found %d matches" % (timeToString(t1-t0),len(results))) #if village seed search if args.sf == None: if len(worldseeds) != 1: sys.exit("ERROR: feature seed search requires exactly one world seed") if not featurechunks: sys.exit("ERROR: feature seed search requires at least one feature location") print("Searching for 32-bit feature seed with world seed %d ..." % (worldseeds[0],)) results = set() t0 = time.time() for magicbase in [0, ((1<<17)-1)]: event = program.test_featureseeds(queue, (1<<31,), None, numpy.int64(magicbase*(1<<31)), numpy.int8(0), hitsBuf, countBuf) event.wait() pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) found = set(hits[h] for h in range(min(16,count[0]))) for s in sorted(found): print(" Match: %d" % (s,)) results.update(found) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) t1 = time.time() print("... OK: Search completed in %s, found %d matches" % (timeToString(t1-t0),len(results))) #if feature seed search if args.sm == None: if len(worldseeds) != 1: sys.exit("ERROR: monument seed search requires exactly one world seed") if not monumentchunks: sys.exit("ERROR: monument seed search requires at least one monument location") print("Searching for 32-bit monument seed with world seed %d ..." % (worldseeds[0],)) results = set() t0 = time.time() for magicbase in [0, ((1<<17)-1)]: event = program.test_monumentseeds(queue, (1<<31,), None, numpy.int64(magicbase*(1<<31)), numpy.int8(0), hitsBuf, countBuf) event.wait() pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) found = set(hits[h] for h in range(min(16,count[0]))) for s in sorted(found): print(" Match: %d" % (s,)) results.update(found) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) t1 = time.time() print("... OK: Search completed in %s, found %d matches" % (timeToString(t1-t0),len(results))) #if monument seed search if args.se == None: if len(worldseeds) != 1: sys.exit("ERROR: end city seed search requires exactly one world seed") if not endcitychunks: sys.exit("ERROR: end city seed search requires at least one end city location") print("Searching for 32-bit end city seed with world seed %d ..." % (worldseeds[0],)) results = set() t0 = time.time() for magicbase in [0, ((1<<17)-1)]: event = program.test_endcityseeds(queue, (1<<31,), None, numpy.int64(magicbase*(1<<31)), numpy.int8(0), hitsBuf, countBuf) event.wait() pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) found = set(hits[h] for h in range(min(16,count[0]))) for s in sorted(found): print(" Match: %d" % (s,)) results.update(found) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) t1 = time.time() print("... OK: Search completed in %s, found %d matches" % (timeToString(t1-t0),len(results))) #if endcity seed search elif worldseeds: print("Validating provided world seeds and locations using OpenCL logic ...") for s in worldseeds: event = program.test_seeds(queue, (1,), None, numpy.int64(s), numpy.int8(0), hitsBuf, countBuf) event.wait() pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) assert(count[0] == 1) assert(hits[0] == s) print(" %d matches all criteria" % (s,)) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) else: print(" %d does not match all criteria" % (s,)) print("... OK") else: if not (slimechunks or villagechunks or monumentchunks or mansionchunks or featurechunks or endcitychunks): sys.exit("ERROR: world seed search requires at least one slime, village, monument, mansion, feature or end city location (preferably a dozen or more)") results = set() pct = -1 if not args.resume: print("Searching for 32-bit (string-derived) world seed suffix ...") t0 = time.time() for seedbase in [0, ((1<<17)-1)]: event = program.test_seeds(queue, (1<<31,), None, numpy.int64(seedbase*(1<<31)), numpy.int8(0), hitsBuf, countBuf) event.wait() pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) found = set(hits[h] for h in range(min(16,count[0]))) for s in sorted(found): print(" Match: %d" % (s,)) results.update(found) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) t1 = time.time() print("... OK: Search completed in %s, found %d matches so far" % (timeToString(t1-t0),len(results))) """ DISABLED timestamp-derived scan; newer JDK uses System.nanoTime() and a secondary Uniquifier() which depends on how many instances of Random() have ever been created print("Searching for ~38-bit (timestamp-derived) world seed suffix ...") t0 = time.time() for seedbase in range(587, int((t0+60*60*24*10) * 1000 / (1<<31))+1): # milliseconds from 1970-01-01 to 2010-01-01 = 1262300400876 ~= 587.8 * 2^31 event = program.test_seeds(queue, (1<<31,), None, numpy.int64(seedbase*(1<<31)), numpy.int8(1), hitsBuf, countBuf) event.wait() pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) found = set(hits[h] for h in range(min(16,count[0]))) for s in sorted(found): print(" Match: %d" % (s,)) results.update(found) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) t1 = time.time() print("... OK: Search completed in %s, found %d matches so far" % (timeToString(t1-t0),len(results))) """ #if not resume print("Searching for 48-bit world seed suffix (ctrl-c to stop) ...") t0 = time.time() pct = 1000 * args.resume // (1<<17) seedbase = args.resume or 1 while seedbase < ((1<<17)-1): try: event = program.test_seeds(queue, (1<<31,), None, numpy.int64(seedbase*(1<<31)), numpy.int8(0), hitsBuf, countBuf) event.wait() if pct != 1000 * seedbase // (1<<17): pct = 1000 * seedbase // (1<<17) pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) if count[0]: pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) found = set(hits[h] for h in range(min(16,count[0]))) for s in sorted(found): print(" Match: %d" % (s,)) results.update(found) count[0] = 0 pyopencl.enqueue_copy(queue, countBuf, count, is_blocking=True) t1 = time.time() print("... %d result(s) after %s, %.1f%% complete, ~%s to go ..." % ( len(results), timeToString(t1-t0), 100.0*seedbase/(1<<17), timeToString(((float((1<<17) - args.resume) / (seedbase - args.resume)) * (t1 - t0)) - (t1 - t0)) )) seedbase += 1 continue except KeyboardInterrupt: pass t1 = time.time() try: input("... PAUSED. Press ENTER to continue, or ctrl-c to exit.") t0 += (time.time() - t1) print("Resuming 48-bit world seed suffix search (ctrl-c to stop) ...") seedbase -= 1 continue except (EOFError, KeyboardInterrupt) as e: pass try: print("\n... OK: Search aborted after %s, resume with --resume %d" % (timeToString(t1-t0), max(args.resume,seedbase-1))) except (EOFError, KeyboardInterrupt) as e: pass seedbase = -1 break #while t1 = time.time() if seedbase >= 0: print("... OK: Search completed in %s" % (timeToString(t1-t0),)) pyopencl.enqueue_copy(queue, count, countBuf, is_blocking=True) pyopencl.enqueue_copy(queue, hits, hitsBuf, is_blocking=True) results.update(hits[h] for h in range(min(16,count[0]))) print("Final results:") for s in sorted(results): print(" %d" % (s,)) print("... OK: %d matches" % (len(results),)) #if worldseeds #if __main__