Scripts:Enhanced rotate demo
Enhancements
July 14, 2005 Enhanced rotate_demo.py by: Dave Lawrence [email protected] Includes updates by: dackz
Changes - Fixed apparent issue where FTP credentials require use of '@' (at least in Win) - Added grossly verbose error logging for troubleshooting FTP problems - Added cfg option "verbose_log" to enable verbose logging - Added verbose log file "rotate_demo_log.txt" * For console debugging, set verbose_log=0 to log to the console - Incorporated local file mods by dackz
Usage: - In Windows it is recommended to use the compiled version of this tool - In ServerSettings.con you would use something like the following - sv.autoDemoHook "adminutils/demo/rotate_demo.exe" - If you did not receive the exe version of this script, you can compile by: * Download ActivePython v2.4 * Install ActivePython * Download py2exe for Python v2.4! * Install py2exe * Place the Enhanced rotate_demo.py in AdminUtils\demo * From the cmd prompt (with python in your PATH), run: python setup.py py2exe * py2exe should execute and create a directory: dist * AdminUtils\demo\dist contains rotate_demo.exe
Downloads http://activestate.com http://starship.python.net/crew/theller/py2exe
Installation
- Browse to
adminutils/demo/
in your bf2 server directory - Backup
rotate_demo.py
. (cp rotate_demo.py rotate_demo.py.bak
.) - Download rotate_demo.py or copy the below text into a file manually.
- If necessary, download the exe and locate in the same place
Operation
General
By default, a log file called rotate_demo_log.txt will created that logs the actions from the prior execution. When there is an error, you can view each operation that completed prio to the error as well as the eeor itself in this file. In addition, rotate_demo_err.txt still clumsily records the same info it did before.
If you are having problems getting the demo moved the appropriate place, I strongly recommend that you run the script manualy from the command line, and outside of the game. The script takes one parameter which is the filename of the demo file. This filename can be of any file. IF you want to check the rotation features, just make sure the test files end with ".bf2demo" for example
cd /games/bf2 touch test1.bf2demo python AdminUtils/demo/rotate_demo.py test1.bf2demo
It is *much* easier to debug this way.
Win BF2 and FTP
For some reason I cannot get the rotate_demo.py script to execute properly under a Windows BF2 server using FTP with an FTP username that use a "@". A popular convention on at least cPanel web hosters is to include the server domain name along with the username, separate with "@". For example:
Host: files.myhost.com User: [email protected] Pwd: blahblah
With a username as shown above, under Windows, the BF2 server is just not parsing the username correctly and the FTP fails. I have no idea why, although it must have something to do with the BF2 version Python. In any case, if you are using Windows, and you have an FTP username like this, I recommend using the compiled version of this script, rotate_demo.exe. This will work.
Code
Downloadable
These urls are out of date and no longer available
The source http://files.fragnastika.net/bf2_rotate_demo/Enhanced/rotate_demo.py
The Win exe version http://files.fragnastika.net/bf2_rotate_demo/Enhanced/rotate_demo.exe
Readable
#! /usr/bin/python # # rotate_demo.py: simple file-rotation and index updating script # # Requires Python 2.3 or newer. # # Theory of operation: # When automatic demo recording is enabled in the BF2 dedicated server it will # call a hook program (such as this) when a new demo file is ready for # publishing. The server will wait for the hook program to complete before # notifying connected clients of the URL the demo can be downloaded from. It is # therefore important that all work is done in a blocking manner in this # program, or clients might try to download demos that aren't in place on the # web server yet. # # Copyright (c)2004 Digital Illusions CE AB # Author: Andreas Fredriksson # # July 14, 2005 # Enhanced rotate_demo.py # by: Dave Lawrence [email protected] # Includes updates by: dackz # # Changes # - Fixed apparent issue where FTP credentials require use of '@' # - Added grossly verbose error logging for troubleshooting FTP problems # - Added cfg option "verbose_log" to enable verbose logging # - Added verbose log file "rotate_demo_log.txt" # * For console debugging, set verbose_log=0 to log to the console # - Incorporated local file mods by dackz # # Usage: # - In Windows it is recommended to use the compiled version of this tool # - In ServerSettings.con you would use something like the following # sv.autoDemoHook "adminutils/demo/rotate_demo.exe" # - If you did not receive the exe version of this script, you can compile by: # * Download ActivePython v2.41 from http://activestate.com # * Install ActivePython # * Download py2exe from http://starship.python.net/crew/theller/py2exe VERSION 2.4! # * Install py2exe # * Place the Enhanced rotate_demo.py in AdminUtils\demo # * From the cmd prompt (with python in your PATH), run: # python setup.py py2exe # * py2exe should execute and create a directory: dist # * AdminUtils\demo\dist contains rotate_demo.exe # import os import sys import shutil # for debugging a hack like this might be useful since stdout and stderr are # discarded when the script is run class writer: def __init__(self): self.stream = open('rotate_demo_log.txt', 'w') def write(self, str): self.stream.write(str) # helper function to create directories as needed -- this doesn't care about # umask or permissions in general so consider this a starting point def ensure_exists(path): try: os.stat(path) except: try: os.makedirs(path) except: pass # set some sane defaults options = { 'use_ftp':'0', 'ftp_server':'', 'ftp_target_dir':'', 'ftp_user':None, 'ftp_password':None, 'target_root': 'webroot', 'file_limit': '10', 'verbose_log': '1', } # parse the config file, if it's there print "Importing cfg\n" try: config = open('rotate_demo.cfg', 'rt') for line_ in config: line = line_.strip() if len(line) == 0 or line.startswith('#'): continue try: key, value = line.split('=') options[key.strip()] = value.strip() except ValueError, ex: print ex except IOError: print "I/O Error on cfg read\n" pass if options['verbose_log'] == '1': sys.stdout = writer() # our first argument indicates the demo file which is ready to be moved path = os.path.normpath(sys.argv[1].replace('"', '')) # handle local file shuffling (web server on same host as bf2 server, or on network share) if options['use_ftp'] == '0': print "Using local file shuffle\n" # this is our target directory (i.e. the download dir) target_demo_dir = os.path.join(options['target_root'], 'demos') # create the directory structure if it doesn't exist ensure_exists(options['target_root']) ensure_exists(os.path.join(options['target_root'], 'demos')) if os.path.abspath(os.path.dirname(path)) != os.path.abspath(target_demo_dir): try: # NOTE: this requires atleast Python 2.3 print "moving '%s' to '%s'" % (path, target_demo_dir) shutil.move(path, target_demo_dir) except IOError: sys.exit(1) timestamped = [] # get a list of .bf2demo files in the target dir (including our own file) for pf in filter(lambda x: x.endswith('.bf2demo'), os.listdir(target_demo_dir)): try: ppath = os.path.join(target_demo_dir, pf) os.chmod(ppath, 0644) # make web-readable timestamped.append((os.stat(ppath).st_mtime, ppath)) except IOError: pass # don't let I/O errors stop us # sort the timestamped file list according to modification time # NOTE: this sort is reversed so that older files are at the end of the list def compare_times(f1, f2): return cmp(f2[0], f1[0]) # note reverse sort order timestamped.sort(compare_times) # delete the oldest files to meet the file limit file_limit = int(options['file_limit']) for timestamp, deletium in timestamped[file_limit:]: try: os.remove(deletium) except IOError: pass # file in use? # create the index file if 0: # dep: I guess this is superfluous idxf = open(os.path.join(options['target_root'], 'index.lst'), 'w') for timestamp, keptfile in timestamped[:file_limit]: fn = keptfile.split(os.sep)[-1] idxf.write('demos/%s\n' % (fn)) idxf.close() else: # use ftp print "Using FTP\n" try: import ftplib import re path.replace('\\\\', '\\') path = os.path.normpath(path).replace('\\', '/') fn = path idx = fn.rfind('/') if idx != -1: fn = fn[idx+1:] try: os.stat(path) except: # This shouldn't happen normally, only when testing from the cmd line print "ERROR- File does not exist: %s\nAborting\n" % (path) raise demof = open(path, 'rb') # set up ftp connection print "Connecting to:\n\tHost: %s\n\tUser: %s\n\t Pwd: %s\n" % (options['ftp_server'], options['ftp_user'], options['ftp_password']) try: ftp = ftplib.FTP(options['ftp_server'], options['ftp_user'], options['ftp_password']) except: print "FTP ERROR: Connect failed!" raise print "Connected ... \n" # change cwd print "Changing cwd to %s\n" % (options['ftp_target_dir']) try: ftp.cwd(options['ftp_target_dir']) except: print "FTP ERROR: failed to cwd => %s" % (options['ftp_target_dir']) raise file_limit = int(options['file_limit']) print "Retrieving file listing from FTP ...\n" try: files = ftp.nlst() except Exception: files = [] files = filter(lambda x: x.endswith('.bf2demo'), files) files.sort() for file in files: print "\t" + file # store the new file print "\nStoring new demo file: %s" % (fn) try: ftp.storbinary('STOR '+fn, demof) except: print "FTP ERROR: Upload failed!" print "\tUpload complete!" demof.close() # Remove the local file print "Removing local demo file %s" % (path) try: # delete local file os.unlink(path) except OSError: # couldn't unlink local file, what to do? print "ERROR- %s Could not be removed!" % (path) pass # handle remote rotation print "\nHandling rotation and removing old FTP demos: file limit = %s" % (file_limit) while len(files) + 1 > file_limit: # dep: nb: this relies on the data formatting in the bf2demo filenames # if you have other print '\tdeleting %s' % (files[0]) try: ftp.delete(files[0]) except: print "FTP ERROR: Unable to delete %s" % (files[0]) pass del files[0] # bye bye ftp.quit() print "\nConnection closed" except Exception, detail: print "\n========================\nROTATE_DEMO ERROR!\n" import traceback log = open('rotate_demo_err.txt', 'w') ex = sys.exc_info() traceback.print_exception(ex[0], ex[1], ex[2], 16, log) print "%s\n%s\n%s\n%s\n%s" % (ex[0], ex[1], ex[2], 16, log) print "========================\n" log.write('\n') log.close() sys.exit(1)