def do_reminders(game_id, dry_run = False): filename = SpinConfig.gamedata_component_filename('dev_reminders.json', override_game_id = game_id) if not os.path.exists(filename): return data = SpinConfig.load(filename) time_now = int(time.time()) sender_name = data['sender'] for reminder in data['reminders']: subject = 'Automated reminder from %s' % sender_name if reminder['body'] == '$XXX': # special case for six-X comments body = get_six_X_comments(game_id, time_now) else: body = 'Due %s %s' % (pretty_print_time(abs(reminder['deadline']-time_now)), 'from now' if reminder['deadline']>time_now else 'ago') + '\n' + reminder['body'] if body: SpinReminders.send_reminders(sender_name, reminder['notify'], subject, body, dry_run = dry_run)
elif key == '--num-clusters': numclusters = int(val) elif key == '--num-sectors': num_sectors = int(val) if '--townhall-level' not in opts: townhall_level = int(baseDifficulty / 2) if townhall_level > 6: townhall_level = 6 unitsInBaseCount = 0 maxUnitsInBaseCount = gamedata['buildings']["toc"]["provides_space"][ townhall_level - 1] # If you need to load an EXISTING base JSON file, here is how to do it: if len(args) > 1: filename = args[1] # SpinConfig.load reads JSON, and strips out comments old_base = SpinConfig.load(filename, stripped=True) # at this point, "old_base" will be a Python data structure (actually a dictionary) # that contains everything that was in the JSON file you loaded print old_base usedClusterXYpairs = [] sceneryList = [ name for name, data in gamedata['inert'].iteritems() if (("desert" in data.get('base_climates', [])) and ( "home" in data.get('base_types', [])) and data.get('auto_spawn', False)) ] buildingsList = [ name for name, data in gamedata['buildings'].iteritems() if (not data.get('developer_only', False)) ]
if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], 'g:', [ 'game-id=', ]) game_id = None for key, val in opts: if key == '--game-id' or key == '-g': game_id = val assert game_id # partial build of gamedata gamedata = { 'predicate_library': SpinConfig.load(args[0]), 'buildings': SpinConfig.load(args[1]), 'tech': SpinConfig.load(args[2]), 'crafting': SpinConfig.load(args[3]) } out_fd = AtomicFileWrite.AtomicFileWrite(args[4], 'w', ident=str(os.getpid())) print >> out_fd.fd, "// AUTO-GENERATED BY invert_requirements.py" # note pluralization of the keys - this matches what UpgradeBar expects requirements = { 'building': prune_dict(
#!/usr/bin/env python # Copyright (c) 2015 SpinPunch. All rights reserved. # Use of this source code is governed by an MIT-style license that can be # found in the LICENSE file. import sys, os, time, getopt, re import SpinJSON # fast JSON library import SpinConfig quarries = SpinConfig.load( SpinConfig.gamedata_component_filename('quarries_compiled.json')) time_now = int(time.time()) def metrics_log_iterator(filename): for line in open(filename).xreadlines(): if '3830_battle_end' not in line: continue event = SpinJSON.loads(line) base_id = event.get('base_id', '') if (not base_id.startswith('q')): continue quarry_id = int(base_id[1:]) yield quarry_id def battle_log_dir_iterator(dirname): log_re = re.compile('^[0-9]+-[0-9]+-vs-[0-9]+-at-(.+).json.*$') for filename in os.listdir(dirname):
if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], 'g:', [ 'game-id=', ]) ident = str(os.getpid()) game_id = None for key, val in opts: if key == '--game-id' or key == '-g': game_id = val assert game_id if game_id != 'mf': # no mods except in old MF sys.exit(0) tech = SpinConfig.load(args[0], stripped=True) out_fd = AtomicFileWrite.AtomicFileWrite(args[1], 'w', ident=ident) print >> out_fd.fd, "// AUTO-GENERATED BY make_tech_mods.py" out = {} MOD_TYPES = [{ 'name': '_health', 'ui_name': ' Health', 'stat': 'max_hp', 'method': '*=(1+strength)', 'strength': [0.01, 0.02, 0.03, 0.04, 0.05], 'ui_description': 'Optimize defensive systems to withstand more damage', 'ui_congrats': 'Defensive systems upgraded for more toughness'
# Copyright (c) 2015 SpinPunch. All rights reserved. # Use of this source code is governed by an MIT-style license that can be # found in the LICENSE file. # this script makes various procedurally-generated inventory items import SpinConfig import SpinJSON import AtomicFileWrite import sys, re, traceback, os, getopt if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], '', []) ident = str(os.getpid()) gamedata = {'resources': SpinConfig.load(args[0])} items_out = AtomicFileWrite.AtomicFileWrite(args[1], 'w', ident=ident) items_fd = items_out.fd spells_out = AtomicFileWrite.AtomicFileWrite(args[2], 'w', ident=ident) spells_fd = spells_out.fd for fd in items_fd, spells_fd: print >> fd, "// AUTO-GENERATED BY make_items_auto.py" out = {'items': [], 'spells': []} # create all iron/water items for resource, resdata in gamedata['resources'].iteritems(): RESOURCE = resource.upper() # names seen by player
import SpinJSON import SpinNoSQL import base64, lz4, SpinLZJB import sys, time, getopt if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], 'g:b:', []) game_id = SpinConfig.game() batch_size = None for key, val in opts: if key == '-g': game_id = val elif key == '-b': batch_size = int(val) gamedata = {} # SpinJSON.load(open(SpinConfig.gamedata_filename())) gamedata['regions'] = SpinConfig.load( SpinConfig.gamedata_component_filename("regions.json", override_game_id=game_id)) nosql_client = SpinNoSQL.NoSQLClient( SpinConfig.get_mongodb_config(SpinConfig.config['game_id'])) TRIALS = 10 region = None region_list = [name for name, data in sorted(gamedata['regions'].items()) if \ data.get('enable_map',1)] total_time = 0.0 for region in region_list: start_time = time.time() db_time = -1
pat_ls.append(pat) pattern = '\\b(' + '|'.join(pat_ls) + ')\\b' flags = 0 if 'i' in config['options']: flags |= re.I # no 'g' option, re is greedy by default self.bad_regex = re.compile(pattern, flags) def is_bad(self, input): return bool(self.bad_regex.search(input)) def censor(self, input): return self.bad_regex.sub(lambda match: '*' * len(match.group()), input) if __name__ == '__main__': import SpinConfig config = SpinConfig.load( SpinConfig.gamedata_component_filename('chat_filter.json')) cf = ChatFilter(config) TESTS = { 'asdf': 'asdf', 'sh!t': '****', 'fu!ckers': '********', 'dwarf shortage': 'dwarf shortage' } for input, expect in TESTS.iteritems(): assert cf.censor(input) == expect print 'OK'
# needs units.json as input for unit names import SpinConfig import SpinJSON import AtomicFileWrite import sys, getopt, os if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], 'g:', ['game-id=']) game_id = SpinConfig.game() for key, val in opts: # allow override of game_id if key == '-g' or key == '--game-id': game_id = val units = SpinConfig.load(args[0]) out_fd = AtomicFileWrite.AtomicFileWrite(args[1], 'w', ident=str(os.getpid())) print >> out_fd.fd, "// AUTO-GENERATED BY make_art_items.py" units = SpinConfig.load(args[0]) out = {} outkeys = [] # create unit inventory icons EXTRA_UNITS = [] # not included in units.json but icons are needed if game_id == 'tr': EXTRA_UNITS += [('ch47', {'art_asset': 'ch47'})]
def do_slave(task): date = task['date'] game_id = task['game_id'] verbose = task['verbose'] dry_run = task['dry_run'] start_time = SpinConfig.cal_to_unix( (int(date[0:4]), int(date[4:6]), int(date[6:8]))) end_time = start_time + 86400 if verbose: print >> sys.stderr, 'converting date', date, 'start_time', start_time, 'end_time', end_time, '...' # gamedata = SpinJSON.load(open(SpinConfig.gamedata_filename(override_game_id = game_id))) if not verbose: filterwarnings('ignore', category=MySQLdb.Warning) quarries = SpinConfig.load( SpinConfig.gamedata_component_filename('quarries_compiled.json')) hives = SpinConfig.load( SpinConfig.gamedata_component_filename('hives_compiled.json')) # ensure that the spawn list is ordered by id_start - necessary for find_template() below for spawn_list in quarries['spawn'], hives['spawn']: spawn_list.sort(key=lambda x: x['id_start']) cfg = SpinConfig.get_mysql_config(game_id + '_upcache') con = MySQLdb.connect(*cfg['connect_args'], **cfg['connect_kwargs']) battles_table = cfg['table_prefix'] + game_id + '_battles' if 0: # find any already-converted battles cur = con.cursor() cur.execute( "SELECT COUNT(*) FROM %s WHERE time >= %%s and time < %%s" % battles_table, (start_time, end_time)) row = cur.fetchone() con.commit() if row and row[0] > 0: print >> sys.stderr, 'there are already', row[ 0], 'entries in this time range, aborting!' return s3 = SpinS3.S3(SpinConfig.aws_key_file()) bucket = 'spinpunch-%sprod-battle-logs' % game_id for entry in s3.list_bucket(bucket, prefix='%s-battles-%s/%s' % (game_id, date[0:6], date)): filename = entry['name'].split('/')[-1] event_time, attacker_id, defender_id, base_id = parse_battle_log_filename( filename) if (not base_id) or event_time < start_time or event_time >= end_time: continue if base_id[0] != 'v': continue # only look at hives print >> sys.stderr, event_time, SpinLog.pretty_time( time.gmtime(event_time)), filename fd = s3.get_open(bucket, entry['name'], allow_keepalive=False) unzipper = subprocess.Popen(['gunzip', '-c', '-'], stdin=fd.fileno(), stdout=subprocess.PIPE) battle_start = None battle_end = None for line in unzipper.stdout.xreadlines(): if '3820_battle_start' in line: battle_start = SpinJSON.loads(line) elif '3830_battle_end' in line: battle_end = SpinJSON.loads(line) if (not battle_start) or (not battle_end): continue base_template = find_template(hives['spawn'], int(base_id[1:])) if not base_template: sys.stderr.write('unknown hive %s\n' % base_id) continue # generate a fake summary summary = { 'time': event_time, 'attacker_id': battle_start['attacker_user_id'], 'attacker_level': battle_start['attacker_level'], 'attacker_outcome': battle_end['battle_outcome'], 'defender_id': battle_start['opponent_user_id'], 'defender_level': battle_start['opponent_level'], 'defender_outcome': 'victory' if battle_end['battle_outcome'] == 'defeat' else 'defeat', 'base_damage': battle_end['base_damage'], 'base_id': battle_start['base_id'], 'base_type': 'hive', 'base_template': base_template, 'loot': battle_end['loot'] } cur = con.cursor() cur.execute( "SELECT battle_id FROM %s WHERE time = %%s and attacker_id = %%s and defender_id = %%s" % battles_table, (event_time, battle_start['attacker_user_id'], battle_start['opponent_user_id'])) row = cur.fetchone() con.commit() if row: sys.stderr.write('appears to be a duplicate, skipping!\n') continue id_generator.set_time(int(time.time())) battle_id = id_generator.generate() # arbitrary keys = [ 'battle_id', ] values = [ battle_id, ] for kname, ktype in battle_fields.iteritems(): path = kname.split(':') probe = summary val = None for i in xrange(len(path)): if path[i] not in probe: break elif i == len(path) - 1: val = probe[path[i]] break else: probe = probe[path[i]] if val is not None: keys.append(kname) values.append(val) query = "INSERT INTO " + battles_table + \ "("+', '.join(['`'+x+'`' for x in keys])+")"+ \ " VALUES ("+', '.join(['%s'] * len(values)) +")" print >> sys.stderr, query print >> sys.stderr, values if not dry_run: cur = con.cursor() cur.execute(query, values) con.commit()
elif hours > 0: if minutes > 0: return '%.1fh' % (float(duration % 86400) / 3600) else: return '%dh' % hours elif minutes > 0: return '%dm' % minutes else: return '' if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], '', []) ident = str(os.getpid()) # read unit data gamedata = {'units': SpinConfig.load(args[0])} out_fd = AtomicFileWrite.AtomicFileWrite(args[1], 'w', ident=ident) # provide special behaviour for unit-type boosts unit_types = {'rover': {'name': 'rover', 'ui_name': 'Infantry', 'ui_name_plural': 'infantry'}, 'transport': {'name': 'transport', 'ui_name': 'Armor', 'ui_name_plural': 'armored units'}, 'starcraft': {'name': 'starcraft', 'ui_name': 'Aircraft', 'ui_name_plural': 'aircraft'}} # templates for different boost types and their properties boost_types = {'damage': {'name': '{unit[name]}_damage_boost_{pct}pct{name_duration}', 'ui_name': '{pct}% {unit[ui_name]} Damage Boost ({ui_name_duration})', 'ui_description': 'Activate to increase damage done by your {unit[ui_name_plural]} by {pct}%. Lasts {ui_description_duration}.', 'icon': 'inventory_damage_{unit[name]}_{icon_color}', 'aura': '{unit[name]}_damage_boosted'}, 'damage_resist': {'name': '{unit[name]}_damage_resist_boost_{pct}pct{name_duration}', 'ui_name': '{pct}% {unit[ui_name]} Toughness Boost ({ui_name_duration})', 'ui_description': 'Activate to reduce damage taken by your {unit[ui_name_plural]} by {pct}%. Lasts {ui_description_duration}.', 'icon': 'inventory_armor_{unit[name]}_{icon_color}', 'aura': '{unit[name]}_damage_resist_boosted'}, 'range': {'name': '{unit[name]}_range_boost_{pct}pct{name_duration}', 'ui_name': '{pct}% {unit[ui_name]} Range Boost ({ui_name_duration})',
if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], 'g:', [ 'game-id=', ]) game_id = None for key, val in opts: if key == '--game-id' or key == '-g': game_id = val assert game_id if game_id != 'mf': # no mods except in old MF sys.exit(0) gamedata = SpinConfig.load(args[0], stripped=True) # main_options.json only tech = SpinConfig.load(args[1], stripped=True) out_fd = AtomicFileWrite.AtomicFileWrite(args[2], 'w', ident=str(os.getpid())) print >> out_fd.fd, "// AUTO-GENERATED BY make_tech_mod_quests.py" out = {} VALUES = [ { 'name': 'health', 'ui_name': 'Health',
#!/usr/bin/env python # Copyright (c) 2015 SpinPunch. All rights reserved. # Use of this source code is governed by an MIT-style license that can be # found in the LICENSE file. # load some standard Python libraries import sys, urllib, urllib2, getopt, socket import SpinConfig import SpinFacebook # just load achievements.json, not all of gamedata, so we can populate before running make-gamedata.sh gamedata = { 'achievements': SpinConfig.load( SpinConfig.gamedata_component_filename('achievements.json')) } def get_endpoint_url(params): port = SpinConfig.config['proxyserver']['external_http_port'] port_str = (':%d' % port) if port != 80 else '' # note: use stable ordering of key/value pairs for the query string, so that the canonical URL is deterministic qs = urllib.urlencode(sorted(params.items(), key=lambda k_v: k_v[0])) return ("http://%s%s/OGPAPI?" % (SpinConfig.config['proxyserver'].get( 'external_host', socket.gethostname()), port_str)) + qs if __name__ == '__main__': opts, args = getopt.gnu_getopt(sys.argv[1:], '', ['dry-run']) dry_run = False
#!/usr/bin/env python # Copyright (c) 2015 SpinPunch. All rights reserved. # Use of this source code is governed by an MIT-style license that can be # found in the LICENSE file. # obsolete AI base analytics tool import SpinJSON import SpinConfig import sys, os, glob, re, gzip, traceback gamedata = SpinJSON.load(open(SpinConfig.gamedata_filename())) gamedata['ai_bases'] = SpinConfig.load( SpinConfig.gamedata_component_filename("ai_bases_compiled.json")) ai_ids = sorted(map(int, gamedata['ai_bases']['bases'].keys())) ai_id_set = set(ai_ids) def get_leveled_quantity(qty, level): if type(qty) == list: return qty[level - 1] return qty def check_bad_units(): for id in ai_ids: base = gamedata['ai_bases']['bases'][str(id)] level = base['resources']['player_level']