├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build.bat ├── images ├── bf2hub-pb-mm-bf2cc │ ├── .dockerignore │ ├── Dockerfile │ └── assets │ │ ├── build │ │ ├── assets.sha512 │ │ ├── assets.txt │ │ ├── bf2 │ │ │ ├── adminutils │ │ │ │ └── demo │ │ │ │ │ └── rotate_demo.py │ │ │ ├── bf2ccd │ │ │ │ ├── autoadmin.xml │ │ │ │ ├── config.xml │ │ │ │ ├── default.profile │ │ │ │ ├── iga.xml │ │ │ │ └── users.xml │ │ │ ├── mods │ │ │ │ └── bf2 │ │ │ │ │ └── settings │ │ │ │ │ ├── modmanager.con │ │ │ │ │ └── serversettings.con │ │ │ ├── pb_amd-64 │ │ │ │ ├── dll │ │ │ │ │ ├── ka001393.so │ │ │ │ │ ├── kc002306.so │ │ │ │ │ └── ks001800.so │ │ │ │ ├── htm │ │ │ │ │ ├── ka001393.htm │ │ │ │ │ ├── kc002306.htm │ │ │ │ │ ├── ks001800.htm │ │ │ │ │ ├── la001393.htm │ │ │ │ │ ├── lc002306.htm │ │ │ │ │ ├── ma001393.htm │ │ │ │ │ ├── mc002306.htm │ │ │ │ │ ├── wa001393.htm │ │ │ │ │ └── wc002306.htm │ │ │ │ ├── pbag.so │ │ │ │ ├── pbags.so │ │ │ │ ├── pbcl.so │ │ │ │ ├── pbcls.so │ │ │ │ ├── pbns.dat │ │ │ │ ├── pbsec.htm │ │ │ │ ├── pbsecsv.htm │ │ │ │ ├── pbsv.cfg │ │ │ │ ├── pbsv.so │ │ │ │ └── pbsvuser_mods_md5.cfg │ │ │ └── rotate_demo.cfg │ │ ├── build.sh │ │ └── extract │ │ └── runtime │ │ ├── nginx │ │ └── default │ │ ├── run.sh │ │ ├── setup.sh │ │ └── www │ │ └── index.html ├── bf2hub-pb-mm-webadmin │ ├── .dockerignore │ ├── Dockerfile │ └── assets │ │ ├── build │ │ ├── assets.sha512 │ │ ├── assets.txt │ │ ├── bf2 │ │ │ ├── admin │ │ │ │ └── modules │ │ │ │ │ └── mm_webadmin.py │ │ │ ├── adminutils │ │ │ │ └── demo │ │ │ │ │ └── rotate_demo.py │ │ │ ├── mods │ │ │ │ └── bf2 │ │ │ │ │ └── settings │ │ │ │ │ ├── modmanager.con │ │ │ │ │ └── serversettings.con │ │ │ ├── pb_amd-64 │ │ │ │ ├── dll │ │ │ │ │ ├── ka001393.so │ │ │ │ │ ├── kc002306.so │ │ │ │ │ └── ks001800.so │ │ │ │ ├── htm │ │ │ │ │ ├── ka001393.htm │ │ │ │ │ ├── kc002306.htm │ │ │ │ │ ├── ks001800.htm │ │ │ │ │ ├── la001393.htm │ │ │ │ │ ├── lc002306.htm │ │ │ │ │ ├── ma001393.htm │ │ │ │ │ ├── mc002306.htm │ │ │ │ │ ├── wa001393.htm │ │ │ │ │ └── wc002306.htm │ │ │ │ ├── pbag.so │ │ │ │ ├── pbags.so │ │ │ │ ├── pbcl.so │ │ │ │ ├── pbcls.so │ │ │ │ ├── pbns.dat │ │ │ │ ├── pbsec.htm │ │ │ │ ├── pbsecsv.htm │ │ │ │ ├── pbsv.cfg │ │ │ │ ├── pbsv.so │ │ │ │ └── pbsvuser_mods_md5.cfg │ │ │ └── rotate_demo.cfg │ │ ├── build.sh │ │ └── extract │ │ └── runtime │ │ ├── nginx │ │ └── default │ │ ├── run.sh │ │ ├── setup.sh │ │ └── www │ │ ├── bf2tool.php │ │ └── index.html ├── bf2hub-pb-mm │ ├── .dockerignore │ ├── Dockerfile │ └── assets │ │ ├── build │ │ ├── assets.sha512 │ │ ├── assets.txt │ │ ├── bf2 │ │ │ ├── adminutils │ │ │ │ └── demo │ │ │ │ │ └── rotate_demo.py │ │ │ ├── mods │ │ │ │ └── bf2 │ │ │ │ │ └── settings │ │ │ │ │ ├── modmanager.con │ │ │ │ │ └── serversettings.con │ │ │ ├── pb_amd-64 │ │ │ │ ├── dll │ │ │ │ │ ├── ka001393.so │ │ │ │ │ ├── kc002306.so │ │ │ │ │ └── ks001800.so │ │ │ │ ├── htm │ │ │ │ │ ├── ka001393.htm │ │ │ │ │ ├── kc002306.htm │ │ │ │ │ ├── ks001800.htm │ │ │ │ │ ├── la001393.htm │ │ │ │ │ ├── lc002306.htm │ │ │ │ │ ├── ma001393.htm │ │ │ │ │ ├── mc002306.htm │ │ │ │ │ ├── wa001393.htm │ │ │ │ │ └── wc002306.htm │ │ │ │ ├── pbag.so │ │ │ │ ├── pbags.so │ │ │ │ ├── pbcl.so │ │ │ │ ├── pbcls.so │ │ │ │ ├── pbns.dat │ │ │ │ ├── pbsec.htm │ │ │ │ ├── pbsecsv.htm │ │ │ │ ├── pbsv.cfg │ │ │ │ ├── pbsv.so │ │ │ │ └── pbsvuser_mods_md5.cfg │ │ │ └── rotate_demo.cfg │ │ ├── build.sh │ │ └── extract │ │ └── runtime │ │ ├── nginx │ │ └── default │ │ ├── run.sh │ │ ├── setup.sh │ │ └── www │ │ └── index.html └── default │ ├── .dockerignore │ ├── Dockerfile │ └── assets │ ├── build │ ├── assets.sha512 │ ├── assets.txt │ ├── bf2 │ │ └── mods │ │ │ └── bf2 │ │ │ └── settings │ │ │ └── serversettings.con │ ├── build.sh │ └── extract │ └── runtime │ ├── run.sh │ └── setup.sh └── local.env /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.sh text eol=lf 4 | *.txt text eol=lf 5 | *.sha512 text eol=lf 6 | extract text eol=lf 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/bf2-linuxded-*.* 2 | **/BF2Hub-Unranked-Linux-*.tar.gz 3 | **/ModManager-*.zip 4 | **/mono-*-installer.bin 5 | **/BF2CCD_*.zip 6 | **/bf2/levels -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Alexander Nihlén 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bf2-docker 2 | 3 | Dockerized Battlefield 2 server based on [insanity54/bf42-dock](https://github.com/insanity54/bf42-dock). The base image is `debian:bullseye-slim` and was tested on Linux containers in Windows 10 WSL2 and Debian 11. Uses multi-stage builds to keep the image sizes down. 4 | 5 | ## Prerequisites 6 | 7 | - [Docker](https://docker.com/) 8 | - [Docker Compose](https://docs.docker.com/compose/) (optional) 9 | 10 | ## Usage 11 | 12 | Different server variations are placed in the [images](https://github.com/nihlen/bf2-docker/tree/master/images) folder. To create your own, you can copy one of the existing images to use as a base, and then place your custom files in the assets/bf2 folder to overwrite any existing files. 13 | 14 | Initial settings or passwords can be set using environment variables. Persisted files like settings, logs and demos are put in the `/volume` directory in the container using symbolic links and should be mapped to a host directory. If you want to have full visibility of the server files you can also map the `/home/bf2/srv` folder of the container. 15 | 16 | To use these images on a remote host like a VPS you can either use the snippets below to build and run or you can build the images locally and then push them to a container registry like Docker Hub or Azure Container Registry (public or private). 17 | 18 | Running multiple servers on the same host can be done by changing the ports in the environment variables and the mapped host port. For this use case I prefer using Docker Compose, an example is listed further down. 19 | 20 | ### [default](https://github.com/nihlen/bf2-docker/tree/master/images/default) 21 | 22 | - Battlefield 2 server (1.5.3153.0) 23 | 24 | The basic image to run a Battlefield 2 server. Not practical since you can't play online but it can be used as a base. 25 | 26 | ``` 27 | docker build -t nihlen/bf2-docker/default https://github.com/nihlen/bf2-docker.git#master:images/default 28 | docker run --name bf2server -v :/volume -p 4711:4711/tcp -p 4712:4712/tcp -p 16567:16567/udp -p 27901:27901/udp -p 29900:29900/udp nihlen/bf2-docker/default:latest 29 | ``` 30 | 31 | ### [bf2hub-pb-mm](https://github.com/nihlen/bf2-docker/tree/master/images/bf2hub-pb-mm) 32 | 33 | - BF2Hub Unranked (R3) 34 | - Updated PunkBuster 35 | - ModManager (2.2c) 36 | - Automatic demo hosting (nginx) 37 | 38 | Uses BF2Hub to play online. The RCON password is set using environment variable `ENV_RCON_PASSWORD`. If you want to persist the demos on the host you can use `-v :/var/www/html/demos` and use `-e ENV_DEMOS_URL=''` to provide demo urls after finished rounds. 39 | 40 | ``` 41 | docker build -t nihlen/bf2-docker/bf2hub-pb-mm https://github.com/nihlen/bf2-docker.git#master:images/bf2hub-pb-mm 42 | docker run --name bf2server -v :/volume -e ENV_RCON_PASSWORD='rconpw123' -e ENV_DEMOS_URL='http://www.example.com:80/' -p 80:80/tcp -p 4711:4711/tcp -p 4712:4712/tcp -p 16567:16567/udp -p 27901:27901/udp -p 29900:29900/udp nihlen/bf2-docker/bf2hub-pb-mm:latest 43 | ``` 44 | 45 | ### [bf2hub-pb-mm-bf2cc](https://github.com/nihlen/bf2-docker/tree/master/images/bf2hub-pb-mm-bf2cc) 46 | 47 | - BF2CC Daemon (1.4.2446) 48 | 49 | Runs with bf2ccd.exe using Mono. The RCON and BF2CC Daemon passwords are set using environment variables `ENV_RCON_PASSWORD` and `ENV_BF2CCD_PASSWORD`. 50 | 51 | ``` 52 | docker build -t nihlen/bf2-docker/bf2hub-pb-mm-bf2cc https://github.com/nihlen/bf2-docker.git#master:images/bf2hub-pb-mm-bf2cc 53 | docker run --name bf2server -it -v :/volume -e ENV_RCON_PASSWORD='rconpw123' -e ENV_BF2CCD_PASSWORD='bf2ccdpw123' -e ENV_DEMOS_URL='http://www.example.com:80/' -p 80:80/tcp -p 4711:4711/tcp -p 4712:4712/tcp -p 16567:16567/udp -p 27901:27901/udp -p 29900:29900/udp nihlen/bf2-docker/bf2hub-pb-mm-bf2cc:latest 54 | ``` 55 | 56 | ### Docker Compose 57 | 58 | To simplify setting up multiple servers on the same host you can use Docker Compose. The `image:` property can point to a locally built image or a URL to your container registry of choice. Note that the game server port and gamespy port need to match in the environment variables and in the Docker port configuration. 59 | 60 | Here is an example of running two servers on the same host: 61 | 62 | ```yaml 63 | version: "3.3" 64 | services: 65 | bf2-docker-1-service: 66 | container_name: bf2-docker-1 67 | image: nihlen/bf2-docker/bf2hub-pb-mm 68 | restart: on-failure 69 | environment: 70 | - ENV_SERVER_NAME=bf2-docker #1 71 | - ENV_MAX_PLAYERS=16 72 | - ENV_SERVER_PORT=16567 73 | - ENV_GAMESPY_PORT=29900 74 | - ENV_DEMOS_URL=http://www.example.com:8000/ 75 | - ENV_RCON_PASSWORD=rconpw123 76 | volumes: 77 | - "/data/bf2/bf2-docker-1/server:/home/bf2/srv" 78 | - "/data/bf2/bf2-docker-1/volume:/volume" 79 | ports: 80 | - "8000:80/tcp" 81 | - "4711:4711/tcp" 82 | - "4712:4712/tcp" 83 | - "16567:16567/udp" 84 | - "27901:27901/udp" 85 | - "29900:29900/udp" 86 | 87 | bf2-docker-2-service: 88 | container_name: bf2-docker-2 89 | image: nihlen/bf2-docker/bf2hub-pb-mm-bf2cc 90 | restart: on-failure 91 | environment: 92 | - ENV_SERVER_NAME=bf2-docker #2 93 | - ENV_MAX_PLAYERS=4 94 | - ENV_SERVER_PORT=16569 95 | - ENV_GAMESPY_PORT=29901 96 | - ENV_DEMOS_URL=http://www.example.com:8001/ 97 | - ENV_RCON_PASSWORD=rconpw123 98 | - ENV_BF2CCD_PASSWORD=bf2ccdpw123 99 | volumes: 100 | - "/data/bf2/bf2-docker-2/server:/home/bf2/srv" 101 | - "/data/bf2/bf2-docker-2/volume:/volume" 102 | ports: 103 | - "8001:80/tcp" 104 | - "4721:4711/tcp" 105 | - "4722:4712/tcp" 106 | - "16569:16569/udp" 107 | - "27911:27901/udp" 108 | - "29901:29901/udp" 109 | ``` 110 | 111 | Place the docker-compose.yml on the host and run `docker-compose up -d --remove-orphans` to create the containers. If you are not using a container registry then the images need to be built on the host first. 112 | 113 | ## Development 114 | 115 | First set up Docker Desktop on Windows (WSL2). 116 | 117 | Download the assets (see assets.txt) and put them in the images/\*/assets/ folder so you don't need to redownload them on each build. Then make your changes in Dockerfile, build.sh, setup.sh and run.sh. Build and run with `.\build.bat`. Make sure Docker is set to use Linux containers. 118 | 119 | Contributions to new or existing images are welcome if you want them public. 120 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | docker stop bf2server 2 | REM docker image rm bf2 3 | REM docker image prune 4 | docker container rm bf2server 5 | docker image build -t bf2 ./images/default 6 | docker run --name bf2server --restart on-failure -it -v e:/docker/container10/server:/home/bf2/srv -v e:/docker/container10/volume:/volume -p 8000:80/tcp -p 4711:4711/tcp -p 4712:4712/tcp -p 16567:16567/udp -p 27901:27901/udp -p 29900:29900/udp --env-file ./local.env bf2:latest 7 | REM docker exec -i -t bf2server /bin/bash 8 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .vscode 3 | build.bat 4 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM debian:bullseye-slim AS build 3 | 4 | # Add assets to image 5 | WORKDIR /home/bf2/tmp 6 | COPY ./assets/build ./ 7 | 8 | # Download and extract server files 9 | RUN bash -x ./build.sh 10 | 11 | # Runtime stage 12 | FROM debian:bullseye-slim AS runtime 13 | WORKDIR /home/bf2/tmp 14 | LABEL maintainer=nihlen 15 | 16 | # Environment variables 17 | ENV SERVER_NAME="bf2-docker" 18 | 19 | # Copy runtime assets 20 | COPY ./assets/runtime ./ 21 | 22 | # Install required packages and set permissions 23 | RUN bash -x ./setup.sh 24 | 25 | # Copy server files from the build stage 26 | COPY --from=build /home/bf2/tmp/srv ./srv 27 | 28 | # Move server files to persisted folder and start server 29 | CMD ./run.sh 30 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/assets.sha512: -------------------------------------------------------------------------------- 1 | b807684116a0f3d2590390567a5a0da8fa9b0804fb9229ae056fa4cad2d8a46cd306d39592a04167c8b757083e4d20a1d28fe04154c25dab3156ef8be9db3702 bf2-linuxded-1.5.3153.0-installer.tgz 2 | 8391cac06f6667ad4cb495e5ed907159b67ac7c9cb5a1d5fa337d363d95d378767382326add388d06c511653a6295241f64eceffc4648b524cede89e0479a84d BF2Hub-Unranked-Linux-R3.tar.gz 3 | d025aff1a6713da0381b8844f64cd016a66ab907bc936e74ca31ef0781424c02597116c322044da46877766318f28c4c4822e5c4bdec1c19a90157c39fe5bbb4 ModManager-v2.2c.zip 4 | c9557048a70e4bbd28a51fa55ce58a87cf652f03329d792621eb22d45dcc9f3f2301cbab0e27944c265ccdd9b8a45d818e1f4dc469c31d5f6fc3df1bbc54cec1 mono-1.1.12.1_0-installer.bin 5 | ee734acba5f3f0fddf3e10e448d03af9de03713425c8844ae31b534eca3904c21abd413d105e7039fde5bd0f855971997ad61caf67fde6156e9b212e426b99d1 BF2CCD_1.4.2446.zip 6 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/assets.txt: -------------------------------------------------------------------------------- 1 | https://www.bf-games.net/downloads/mirror/2956 bf2-linuxded-1.5.3153.0-installer.tgz 2 | https://www.bf2hub.com/downloads/BF2Hub-Unranked-Linux-R3.tar.gz 3 | https://static.nihlen.net/bf2/server/ModManager-v2.2c.zip 4 | https://download.mono-project.com/archive/1.1.12.1/linux-installer/0/mono-1.1.12.1_0-installer.bin 5 | https://www.fullcontactwar.com/files/BF2CCD_1.4.2446.zip 6 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/adminutils/demo/rotate_demo.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # rotate_demo.py: simple file-rotation and index updating script 4 | # 5 | # Requires Python 2.3 or newer. 6 | # 7 | # Theory of operation: 8 | # When automatic demo recording is enabled in the BF2 dedicated server it will 9 | # call a hook program (such as this) when a new demo file is ready for 10 | # publishing. The server will wait for the hook program to complete before 11 | # notifying connected clients of the URL the demo can be downloaded from. It is 12 | # therefore important that all work is done in a blocking manner in this 13 | # program, or clients might try to download demos that aren't in place on the 14 | # web server yet. 15 | # 16 | # Copyright (c)2004 Digital Illusions CE AB 17 | # Author: Andreas Fredriksson 18 | 19 | import os 20 | import sys 21 | import shutil 22 | 23 | # for debugging a hack like this might be useful since stdout and stderr are 24 | # discarded when the script is run 25 | # class writer: 26 | # def __init__(self): 27 | # self.stream = open('log.txt', 'w') 28 | # def write(self, str): 29 | # self.stream.write(str) 30 | # 31 | #sys.stdout = writer() 32 | 33 | # helper function to create directories as needed -- this doesn't care about 34 | # umask or permissions in general so consider this a starting point 35 | 36 | 37 | def ensure_exists(path): 38 | try: 39 | os.stat(path) 40 | except: 41 | try: 42 | os.makedirs(path) 43 | except: 44 | pass 45 | 46 | 47 | # set some sane defaults 48 | options = { 49 | 'use_ftp': '0', 50 | 'ftp_server': '', 51 | 'ftp_target_dir': '', 52 | 'ftp_user': None, 53 | 'ftp_password': None, 54 | 'target_root': 'webroot', 55 | 'file_limit': '10', 56 | } 57 | 58 | # parse the config file, if it's there 59 | try: 60 | config = open('rotate_demo.cfg', 'rt') 61 | for line_ in config: 62 | line = line_.strip() 63 | if len(line) == 0 or line.startswith('#'): 64 | continue 65 | try: 66 | key, value = line.split('=') 67 | options[key.strip()] = value.strip() 68 | except ValueError, ex: 69 | print ex 70 | except IOError: 71 | pass 72 | 73 | # our first argument indicates the demo file which is ready to be moved 74 | path = os.path.normpath(sys.argv[1].replace('"', '')) 75 | 76 | # handle local file shuffling (web server on same host as bf2 server, or on network share) 77 | if options['use_ftp'] == '0': 78 | # this is our target directory (i.e. the download dir) 79 | target_demo_dir = os.path.join(options['target_root'], 'demos/uploaded') 80 | 81 | # create the directory structure if it doesn't exist 82 | ensure_exists(options['target_root']) 83 | ensure_exists(os.path.join(options['target_root'], 'demos/uploaded')) 84 | 85 | # don't move if path and target are the same 86 | if os.path.abspath(os.path.dirname(path)) != os.path.abspath(target_demo_dir): 87 | try: 88 | # NOTE: this requires atleast Python 2.3 89 | print "moving '%s' to '%s'" % (path, target_demo_dir) 90 | shutil.move(path, target_demo_dir) 91 | except IOError: 92 | sys.exit(1) 93 | 94 | timestamped = [] 95 | 96 | # get a list of .bf2demo files in the target dir (including our own file) 97 | for pf in filter(lambda x: x.endswith('.bf2demo'), os.listdir(target_demo_dir)): 98 | try: 99 | ppath = os.path.join(target_demo_dir, pf) 100 | os.chmod(ppath, 0644) # make web-readable 101 | timestamped.append((os.stat(ppath).st_mtime, ppath)) 102 | except IOError: 103 | pass # don't let I/O errors stop us 104 | 105 | # sort the timestamped file list according to modification time 106 | # NOTE: this sort is reversed so that older files are at the end of the list 107 | def compare_times(f1, f2): return cmp(f2[0], f1[0]) # note reverse sort order 108 | timestamped.sort(compare_times) 109 | 110 | # delete the oldest files to meet the file limit 111 | file_limit = int(options['file_limit']) 112 | for timestamp, deletium in timestamped[file_limit:]: 113 | try: 114 | os.remove(deletium) 115 | except IOError: 116 | pass # file in use? 117 | 118 | # create the index file 119 | if 0: # dep: I guess this is superfluous 120 | idxf = open(os.path.join(options['target_root'], 'index.lst'), 'w') 121 | for timestamp, keptfile in timestamped[:file_limit]: 122 | fn = keptfile.split(os.sep)[-1] 123 | idxf.write('demos/%s\n' % (fn)) 124 | idxf.close() 125 | 126 | else: # use ftp 127 | try: 128 | import ftplib 129 | import re 130 | 131 | path.replace('\\\\', '\\') 132 | 133 | path = os.path.normpath(path).replace('\\', '/') 134 | 135 | fn = path 136 | idx = fn.rfind('/') 137 | if idx != -1: 138 | fn = fn[idx+1:] 139 | 140 | demof = open(path, 'rb') 141 | 142 | # set up ftp connection and change cwd 143 | ftp = ftplib.FTP(options['ftp_server'], options['ftp_user'], options['ftp_password']) 144 | ftp.cwd(options['ftp_target_dir']) 145 | 146 | file_limit = int(options['file_limit']) 147 | 148 | try: 149 | files = ftp.nlst() 150 | except Exception: 151 | files = [] 152 | files = filter(lambda x: x.endswith('.bf2demo'), files) 153 | files.sort() 154 | 155 | # store the new file 156 | ftp.storbinary('STOR '+fn, demof) 157 | 158 | demof.close() 159 | 160 | try: 161 | # delete local file 162 | os.unlink(path) 163 | except OSError: 164 | # couldn't unlink local file, what to do? 165 | pass 166 | 167 | # handle rotation 168 | while len(files) + 1 > file_limit: 169 | # dep: nb: this relies on the data formatting in the bf2demo filenames 170 | # if you have other 171 | #print 'deleting %s' % (files[0]) 172 | ftp.delete(files[0]) 173 | del files[0] 174 | 175 | # bye bye 176 | ftp.quit() 177 | 178 | except Exception, detail: 179 | import traceback 180 | log = open('rotate_demo_err.txt', 'w') 181 | ex = sys.exc_info() 182 | traceback.print_exception(ex[0], ex[1], ex[2], 16, log) 183 | log.write('\n') 184 | log.close() 185 | sys.exit(1) 186 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/bf2ccd/autoadmin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | false 6 | 350 7 | true 8 | false 9 | 2 10 | false 11 | false 12 | 5 13 | true 14 | 1 15 | 1 16 | 17 | false 18 | YourClanTag1,YourClanTag2 19 | false 20 | false 21 | 0 22 | 0 23 | false 24 | -10 25 | false 26 | 27 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/bf2ccd/config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | /home/bf2/srv/ 6 | start_bf2hub.sh 7 | 8 | true 9 | default 10 | 0.0.0.0 11 | 4712 12 | bf2 13 | false 14 | false 15 | 64 16 | -noquitprompts -autostart 17 | 18 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/bf2ccd/default.profile: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1 5 | 0.0.0.0 6 | {{server_port}} 7 | {{gamespy_port}} 8 | 0.0.0.0 9 | 4711 10 | true 11 | false 12 | true 13 | {{rcon_password}} 14 | 15 | 16 | 1 17 | bf2-docker 18 | 19 | true 20 | true 21 | true 22 | true 23 | 0 24 | 64 25 | 2 26 | 15 27 | 15 28 | 15 29 | 15 30 | 100 31 | 3 32 | 0 33 | 0 34 | 100 35 | 100 36 | 100 37 | 100 38 | false 39 | 0 40 | false 41 | true 42 | 90 43 | 2 44 | 30 45 | false 46 | 100 47 | true 48 | http:// 49 | http:// 50 | adminutils/demo/rotate_demo.py 51 | modmanager 52 | 53 | 54 | 55 | 0 56 | 15 57 | 58 | 15 59 | true 60 | true 61 | 6 62 | 6 63 | 30 64 | 10 65 | true 66 | 0 67 | bf2 68 | 0 69 | 2 70 | 71 | 16 72 | 50 73 | 50 74 | false 75 | false 76 | 77 | 78 | 1 79 | 0 80 | 2 81 | 1 82 | 1 83 | -4 84 | 1 85 | 2 86 | 1 87 | 2 88 | 1 89 | 5 90 | 2 91 | 4 92 | 5 93 | 1 94 | -1 95 | -2 96 | 100 97 | 100 98 | 100 99 | 50 100 | 25 101 | 102 | 103 | 1 104 | 1 105 | strike_at_karkand 106 | gpm_cq 107 | 32 108 | 109 | 110 | 1 111 | false 112 | 113 | 55125 114 | 55123 115 | 55124 116 | 117 | 3 118 | true 119 | 120 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/bf2ccd/iga.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | tk 5 | <Action> <Player> for Team Killing! 6 | 7 | 8 | language 9 | <Action> <Player> Please Watch Your Language! 10 | 11 | 12 | vwhore 13 | <Action> <Player> for Stealing/Shooting Teammates Vehicles! 14 | 15 | 16 | spam 17 | <Action> <Player> Stop Spamming Messages! 18 | 19 | 20 | padding 21 | <Action> <Player> for CHEATING --> Padding Stats 22 | 23 | 24 | member 25 | <Action> <Player> to make room for an Admin/Member 26 | 27 | 28 | smack 29 | <Action> <Player> for being a SMACKTARD! Knock it OFF! 30 | 31 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/bf2ccd/users.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | admin 5 | {{bf2ccd_password}} 6 | true 7 | Administrator account 8 | Administrators 9 | 10 | 11 | Administrators 12 | r_Administrator 13 | 1 14 | 15 | 16 | Administrators 17 | r_CreateProfiles 18 | 1 19 | 20 | 21 | Administrators 22 | r_EditProfiles 23 | 1 24 | 25 | 26 | Administrators 27 | r_EditBF2Settings 28 | 1 29 | 30 | 31 | Administrators 32 | r_EditMapList 33 | 1 34 | 35 | 36 | Administrators 37 | r_EditVOIPSettings 38 | 1 39 | 40 | 41 | Administrators 42 | r_EditScoreSettings 43 | 1 44 | 45 | 46 | Administrators 47 | r_EditAutoAdminSettings 48 | 1 49 | 50 | 51 | Administrators 52 | r_EditAutoMessages 53 | 1 54 | 55 | 56 | Administrators 57 | r_LoadProfiles 58 | 1 59 | 60 | 61 | Administrators 62 | r_ShutdownServer 63 | 1 64 | 65 | 66 | Administrators 67 | r_RestartServer 68 | 1 69 | 70 | 71 | Administrators 72 | r_Ban 73 | 1 74 | 75 | 76 | Administrators 77 | p_MaxBanTime 78 | 0 79 | 80 | 81 | Administrators 82 | r_ManageBanList 83 | 1 84 | 85 | 86 | Administrators 87 | r_Kick 88 | 1 89 | 90 | 91 | Administrators 92 | r_ChangeMap 93 | 1 94 | 95 | 96 | Administrators 97 | r_CustomCommands 98 | 1 99 | 100 | 101 | Administrators 102 | r_DaemonOptions 103 | 1 104 | 105 | 106 | Administrators 107 | 108 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/mods/bf2/settings/modmanager.con: -------------------------------------------------------------------------------- 1 | # 2 | # Multiplay, ModManager 3 | # 4 | modmanager.autoSave 1 5 | modmanager.banManagerModule "mm_banmanager" 6 | modmanager.debugEnable 0 7 | modmanager.debugFile "modmanager_debug.log" 8 | modmanager.homeGuess "C:/Documents and Settings/Administrator/My Documents/Battlefield 2/" 9 | modmanager.logAppend 0 10 | modmanager.logAutoFlush 1 11 | modmanager.logDateFormat "[%Y-%m-%d %H:%M:%S] " 12 | modmanager.logLevel 2 13 | modmanager.logModule "mm_logger" 14 | modmanager.moduleBase "modules" 15 | modmanager.rconModule "mm_rcon" 16 | 17 | # Modules 18 | #modmanager.loadModule "mm_tk_punish" 19 | #modmanager.loadModule "mm_kicker" 20 | #modmanager.loadModule "mm_announcer" 21 | modmanager.loadModule "mm_bf2cc" 22 | modmanager.loadModule "mm_autobalance" 23 | #modmanager.loadModule "mm_reserver" 24 | modmanager.loadModule "mm_iga" 25 | 26 | # 27 | # ModManager Announcer 28 | # 29 | #mm_announcer.addTimedMessage "30:300:Server Rules: No team killing, no stats padding, keep the teams balanced and play fair!" 30 | 31 | # 32 | # ModManager Team autobalance 33 | # 34 | mm_autobalance.allowCommander 0 35 | mm_autobalance.allowSquadLeader 0 36 | mm_autobalance.allowSquadMember 0 37 | mm_autobalance.roundSwitch 1 38 | 39 | # 40 | # BF2CC for ModManager 41 | # 42 | mm_bf2cc.chatBufferSize 50 43 | mm_bf2cc.serverChatFormat "[Admin: %s] %s" 44 | 45 | # 46 | # ModManager Logger 47 | # 48 | mm_logger.logAppend 0 49 | mm_logger.logAutoFlush 1 50 | mm_logger.logFilename "modmanager.log" 51 | 52 | # 53 | # ModManager Player Kicker 54 | # 55 | mm_kicker.banLimit 1 56 | mm_kicker.banPeriod "Round" 57 | mm_kicker.banWordReason "Using bad / racist language" 58 | mm_kicker.enableChatChecks 0 59 | mm_kicker.idleIgnoreNotStarted 1 60 | mm_kicker.idleLimit 0 61 | mm_kicker.initDelay 60 62 | mm_kicker.kickDelay 5 63 | mm_kicker.kickLimit 3 64 | mm_kicker.kickMessage "Sorry '%s' your are being kicked ( %s )" 65 | mm_kicker.kickType 1 66 | mm_kicker.kickWordReason "Using bad / racist language" 67 | mm_kicker.maxPing 0 68 | mm_kicker.minPing 0 69 | mm_kicker.negScoreKick 0 70 | mm_kicker.pingLimit 0 71 | mm_kicker.positionDelay 120 72 | mm_kicker.samplePeriod 120 73 | mm_kicker.sampleRate 120 74 | mm_kicker.warnWordMessage "WARNING: Please refrain from using bad / racist language on this server '%s'" 75 | mm_kicker.chatSpamLimit 100 76 | mm_kicker.chatSpamPeriod 1 77 | 78 | # 79 | # ModManager Rcon 80 | # 81 | mm_rcon.allowBatching 1 82 | mm_rcon.basicAuthLevel 50 83 | mm_rcon.enableLinger 0 84 | mm_rcon.lingerFor 1 85 | mm_rcon.logCommands 0 86 | mm_rcon.loginMessage "" 87 | mm_rcon.logoutMessage "" 88 | mm_rcon.rconBasicPassword "{{rcon_password}}" 89 | mm_rcon.rconIp "0.0.0.0" 90 | mm_rcon.rconListenQueue 1 91 | mm_rcon.rconPassword "{{rcon_password}}" 92 | mm_rcon.rconPort 4711 93 | mm_rcon.reuseAddress 1 94 | mm_rcon.superAuthLevel 100 95 | mm_rcon.defaultGametype "gpm_cq" 96 | mm_rcon.advancedMapSizeValidation 0 97 | 98 | # 99 | # ModManager Reserver 100 | # 101 | mm_reserver.kickDelay 5 102 | mm_reserver.kickReason "Reserved slots reached" 103 | mm_reserver.kickMode 2 104 | mm_reserver.kickType 1 105 | mm_reserver.privatePassword "" 106 | mm_reserver.reservedSlots 1 107 | 108 | # 109 | # ModManager Team kill punisher 110 | # 111 | mm_tk_punish.announcePunishments 1 112 | mm_tk_punish.banMessageDelay 5 113 | mm_tk_punish.bannedBy "ModManager Team Kill Punisher" 114 | mm_tk_punish.banPeriod "Round" 115 | mm_tk_punish.banReason "Team killing" 116 | mm_tk_punish.forgiveMessage "TKPUNISH: %s forgives %s for a teamkill (%s has %d punishes and %d forgives)" 117 | mm_tk_punish.punishMessage "TKPUNISH: %s punishes %s for a teamkill (%s has %d punishes and %d forgives)" 118 | mm_tk_punish.punishTime 20 119 | 120 | # 121 | # ModManager BanManager 122 | # 123 | mm_banmanager.banFilename "mm_bans.xml" 124 | mm_banmanager.banMessage "%s you are being banned (reason: %s)" 125 | mm_banmanager.defaultBanAddress "N/A" 126 | mm_banmanager.defaultBanCdKeyHash "N/A" 127 | mm_banmanager.defaultBanDelay 5 128 | mm_banmanager.defaultBanMethod "Key" 129 | mm_banmanager.defaultBanNick "N/A" 130 | mm_banmanager.defaultBanPeriod "Perm" 131 | mm_banmanager.defaultBanReason "Unknown" 132 | mm_banmanager.defaultKickDelay 5 133 | mm_banmanager.defaultKickReason "Unknown" 134 | mm_banmanager.defaultUnBanReason "Unknown" 135 | mm_banmanager.kickMessage "%s you are being kicked (reason: %s)" 136 | mm_banmanager.dateTimeFormat "%d/%m/%Y %H:%M:%S %Z" 137 | mm_banmanager.oldDateTimeFormat "%a %b %d %H:%M:%S %Y" 138 | 139 | # 140 | # In Game Admin v1.6 141 | # 142 | mm_iga.addAdmin "d975d59a9b32e9f105a15667a18e93d7:all" 143 | mm_iga.authLevel 100 144 | mm_iga.addCmdBinding "k|kick:iga kick" 145 | mm_iga.addCmdBinding "b|ban:iga ban" 146 | mm_iga.addCmdBinding "m|map:map" 147 | mm_iga.addCmdBinding "s|say:exec game.sayAll" 148 | mm_iga.addCmdBinding "sw|switch:bf2cc switchplayer" 149 | mm_iga.addCmdBinding "w|warn:iga warn" 150 | mm_iga.addCmdBinding "r|restart:admin.restartMap" 151 | mm_iga.addCmdBinding "n|next:admin.runNextLevel" 152 | mm_iga.addCmdBinding "l|list:admin.listPlayers" 153 | mm_iga.addCmdBinding "p|pause:exec gameLogic.togglePause" 154 | mm_iga.cmdPrefix "!" 155 | mm_iga.notAdminMessage "Sorry %s you are not registered as an admin!" 156 | mm_iga.notAuthedMessage "Sorry %s you are not permitted use the command %s" 157 | mm_iga.warningAction "Warning" 158 | mm_iga.warningPrefix "[%admin%] " 159 | mm_iga.addWarning "tk|team killing:%action% %player% stop Team Killing!" 160 | mm_iga.addWarning "lang|language:%action% %player% stop using Bad Language!" 161 | mm_iga.addWarning "vh|vehicle whore:%action% %player% stop Stealing/Shooting Teammates Vehicles!" 162 | mm_iga.addWarning "hp|high ping:%action% %player% High Ping!" 163 | mm_iga.addWarning "spam|spamming:%action% %player% stop Spamming Messages!" 164 | mm_iga.addWarning "i|idle:%action% %player% for being Idle!" 165 | mm_iga.addWarning "sp|Cheating Stats:%action% %player% stop Stat Padding!" 166 | mm_iga.addWarning "mr|make room for admin:%action% %player% to make room for an Admin" 167 | mm_iga.addWarning "nv|name violation:%action% %player% for Name Violation!" 168 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/mods/bf2/settings/serversettings.con: -------------------------------------------------------------------------------- 1 | sv.serverName "{{server_name}}" 2 | sv.password "" 3 | sv.internet 1 4 | sv.serverIP "0.0.0.0" 5 | sv.serverPort {{server_port}} 6 | sv.welcomeMessage "" 7 | sv.punkBuster 1 8 | sv.allowFreeCam 1 9 | sv.allowExternalViews 1 10 | sv.allowNoseCam 1 11 | sv.hitIndicator 1 12 | sv.maxPlayers {{max_players}} 13 | sv.numPlayersNeededToStart 2 14 | sv.notEnoughPlayersRestartDelay 15 15 | sv.startDelay 15 16 | sv.endDelay 15 17 | sv.spawnTime 15 18 | sv.manDownTime 15 19 | sv.endOfRoundDelay 15 20 | sv.ticketRatio 100 21 | sv.roundsPerMap 3 22 | sv.timeLimit 0 23 | sv.scoreLimit 0 24 | sv.soldierFriendlyFire 100 25 | sv.vehicleFriendlyFire 100 26 | sv.soldierSplashFriendlyFire 100 27 | sv.vehicleSplashFriendlyFire 100 28 | sv.tkPunishEnabled 0 29 | sv.tkNumPunishToKick 0 30 | sv.tkPunishByDefault 0 31 | sv.votingEnabled 1 32 | sv.voteTime 90 33 | sv.minPlayersForVoting 2 34 | sv.teamVoteOnly 0 35 | sv.gameSpyPort {{gamespy_port}} 36 | sv.allowNATNegotiation 0 37 | sv.interfaceIP "0.0.0.0" 38 | sv.autoRecord 0 39 | sv.demoIndexURL {{demos_url}} 40 | sv.demoDownloadURL {{demos_url}} 41 | sv.autoDemoHook "adminutils/demo/rotate_demo.py" 42 | sv.demoQuality 10 43 | sv.adminScript "modmanager" 44 | sv.timeBeforeRestartMap 5 45 | sv.autoBalanceTeam 0 46 | sv.teamRatioPercent 100 47 | sv.voipEnabled 1 48 | sv.voipQuality 5 49 | sv.voipServerRemote 0 50 | sv.voipServerRemoteIP "" 51 | sv.voipServerPort 55126 52 | sv.voipBFClientPort 55123 53 | sv.voipBFServerPort 55124 54 | sv.voipSharedPassword "" 55 | sv.useGlobalRank 0 56 | sv.useGlobalUnlocks 1 57 | sv.sponsorText "" 58 | sv.sponsorLogoURL "" 59 | sv.communityLogoURL "" 60 | sv.radioSpamInterval 6 61 | sv.radioMaxSpamFlagCount 6 62 | sv.radioBlockedDurationTime 30 63 | sv.numReservedSlots 0 64 | sv.friendlyFireWithMines 0 65 | sv.coopBotCount 32 66 | sv.coopBotDifficulty 50 67 | sv.coopBotRatio 50 68 | sv.noVehicles 0 69 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/dll/ka001393.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/dll/ka001393.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/dll/kc002306.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/dll/kc002306.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/dll/ks001800.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/dll/ks001800.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/htm/ma001393.htm: -------------------------------------------------------------------------------- 1 | Not Yet Supported -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/htm/mc002306.htm: -------------------------------------------------------------------------------- 1 | Not Yet Supported -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbag.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbag.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbags.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbags.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbcl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbcl.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbcls.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbcls.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbns.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbns.dat -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbsec.htm: -------------------------------------------------------------------------------- 1 |

2 |

To Manually update your local PunkBuster Security Files, 3 | choose "Save As" from your Web Browser's "File" Menu and explore to 4 | find your game folder and the subfolder called "pb", then save this file 5 | in that location (/pb/pbsec.htm or /pb/pbsecsv.htm). 6 | The next time you start your game, your PunkBuster will have the latest 7 | security information necessary to accept auto-updates from PB Servers. 8 |

9 |

S 3 MASTER5.EVENBALANCE.COM 16567 0 10 |

S 4 MASTER6.EVENBALANCE.COM 16567 0 11 |

S 5 MASTER7.EVENBALANCE.COM 16567 0 12 |

S 2 BF21.EVENBALANCE.COM 16567 0 13 |

M BF22.EVENBALANCE.COM 16567 14 |

F A K 1393 7B60 48AB9BE59074C258CE2C758DD38F5F2F E14C9AF0134D6C493FEFACD9F684C0B5 15 |

F C K 2306 76CF0 EB739E71D5C5758D9749199CA2DA3BDF 22C8077205319247CD9DE1FE07AAC833 16 |

F S K 1800 BF0F0 8F9302F5BD776347AA15D93B658CFA9D D366F0E8009DF62E4433226F73C7F739 17 |

F A L 1393 112BC F3D5B051895295547B25B730A5532DAB 8F5F431A43F8D3C45D6AF9694D9BDBFC 18 |

F C L 2306 DF764 3FA4720F15D5323180E7EC6740D5DD00 640CFF763A0B491BD430082FEBEBC098 19 |

F S L 1800 144244 60CE7BF03014B2109BF18094B7807937 77040B56295447BC5CBF300034F460ED 20 |

F A M 1393 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 21 |

F C M 2306 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 22 |

F S M 1800 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 23 |

F A W 1393 10000 2EB9DF6C1CA87B2DAD817BF3B66939D4 839BDB6CDCB638290C1C645F30A5FFB6 24 |

F C W 2306 E9F54 019D54A9CD7385B9F32EAE49F87B3493 ED8C3FE257A1EFAC3705394C9A441195 25 |

F S W 1800 66000 B5DA0C51C5806C17696B1C5E11B11855 856849563D318794E1C67FA1B4A027F3 26 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbsecsv.htm: -------------------------------------------------------------------------------- 1 |

2 |

To Manually update your local PunkBuster Security Files, 3 | choose "Save As" from your Web Browser's "File" Menu and explore to 4 | find your game folder and the subfolder called "pb", then save this file 5 | in that location (/pb/pbsec.htm or /pb/pbsecsv.htm). 6 | The next time you start your game, your PunkBuster will have the latest 7 | security information necessary to accept auto-updates from PB Servers. 8 |

9 |

S 3 MASTER5.EVENBALANCE.COM 16567 0 10 |

S 4 MASTER6.EVENBALANCE.COM 16567 0 11 |

S 5 MASTER7.EVENBALANCE.COM 16567 0 12 |

S 2 BF21.EVENBALANCE.COM 16567 0 13 |

M BF22.EVENBALANCE.COM 16567 14 |

F A K 1393 7B60 48AB9BE59074C258CE2C758DD38F5F2F E14C9AF0134D6C493FEFACD9F684C0B5 15 |

F C K 2306 76CF0 EB739E71D5C5758D9749199CA2DA3BDF 22C8077205319247CD9DE1FE07AAC833 16 |

F S K 1800 BF0F0 8F9302F5BD776347AA15D93B658CFA9D D366F0E8009DF62E4433226F73C7F739 17 |

F A L 1393 112BC F3D5B051895295547B25B730A5532DAB 8F5F431A43F8D3C45D6AF9694D9BDBFC 18 |

F C L 2306 DF764 3FA4720F15D5323180E7EC6740D5DD00 640CFF763A0B491BD430082FEBEBC098 19 |

F S L 1800 144244 60CE7BF03014B2109BF18094B7807937 77040B56295447BC5CBF300034F460ED 20 |

F A M 1393 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 21 |

F C M 2306 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 22 |

F S M 1800 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 23 |

F A W 1393 10000 2EB9DF6C1CA87B2DAD817BF3B66939D4 839BDB6CDCB638290C1C645F30A5FFB6 24 |

F C W 2306 E9F54 019D54A9CD7385B9F32EAE49F87B3493 ED8C3FE257A1EFAC3705394C9A441195 25 |

F S W 1800 66000 B5DA0C51C5806C17696B1C5E11B11855 856849563D318794E1C67FA1B4A027F3 26 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbsv.cfg: -------------------------------------------------------------------------------- 1 | ; ------------------------------ 2 | ; General Settings 3 | ; ------------------------------ 4 | pb_sv_MsgPrefix "PunkBuster Server" 5 | pb_sv_MaxDlRate 4 //[KB/sec requested per file (default=4)] 6 | pb_sv_MaxConDls 6 //[Concurrent downloads (default=1)] 7 | pb_sv_MaxSendRate 8 // [speed at which PB Server sends pbcl and pbag updates in KBps (default=8)] 8 | pb_sv_CQC 1 //[0=No, 1=Yes (default=1)] 9 | pb_sv_Sleep 60 //[# of Milliseconds (default=60)] 10 | pb_sv_PowerMin 10 //[Power Points] 11 | pb_sv_PowerDef 1 //[Power Points] 12 | pb_sv_PowerKickLen 5 //[Minutes (default=5)] 13 | pb_sv_DupNameGrace 0 //[Seconds] 14 | pb_sv_ExtChar 0 //[0=No, 1=Yes (default=0)] 15 | pb_sv_GuidRelax 7 //[1=UNKN, 2=WRONGIP, 4=DUP (add desired values)] 16 | pb_sv_RconReload 0 //[0=No, 1=Yes (default=0)] 17 | pb_sv_LogSync 0 //[0=No, 1=Yes (default=0)] 18 | pb_sv_AutoUpdBan 1 //[0=No, 1=Yes (default=0)] 19 | 20 | 21 | ; ------------------------------ 22 | ; Other Settings 23 | ; ------------------------------ 24 | pb_sv_Restrictions 1 //[Kick for Restrictions 0=No, 1=Yes (default=1) 2=key macro Restrictions] 25 | pb_sv_FileWhitelist "" //[Folder Filename Filename ... Filename] 26 | pb_sv_EmptyName 0 //[0=No, 1=Yes (default=0)] 27 | pb_sv_LogFloor 1 //[Low log filename serial #] 28 | pb_sv_MinName 0 //[Min Characters in Player name (default=0)] 29 | pb_sv_MaxName 0 //[Max Characters in Player name (default=0)] 30 | pb_sv_LanMask "" //[IP Address Mask for LAN Players (default=)] 31 | pb_sv_Lan 0 //[0=No, 1=Yes (default=0)] 32 | pb_sv_ChangePeriod 600 // For pb_sv_ChangeMax 33 | pb_sv_ChangeMax 5 // [Max name changes allowed in pb_sv_ChangePeriod] ex 5 changes in 600 seconds 34 | 35 | 36 | ; ------------------------------ 37 | ; Kick Settings 38 | ; ------------------------------ 39 | pb_sv_KickLen 1 //[Minutes (default=2)] 40 | pb_sv_ScoreKick 0 //[Min score (negative) ] 41 | pb_sv_UpdateGrace 2400 //[Seconds to wait before Update Failure kick] 42 | pb_sv_NoGuidGrace 10 //[Seconds to wait before No GUID kick] 43 | 44 | 45 | ; ------------------------------ 46 | ; PBSS Settings 47 | ; ------------------------------ 48 | pb_sv_AutoSs 0 //[0=No, 1=Yes (default=0)] 49 | pb_sv_SsFloor 1 //[Low screenshot filename serial #] 50 | pb_sv_SsCeiling 500 //[High screenshot filename serial #] 51 | pb_sv_AutoSsFrom 200 //[Min # of seconds to wait before requesting next ss] 52 | pb_sv_AutoSsTo 1200 //[Max # of seconds to wait before requesting next ss] 53 | pb_sv_SsCmd "" //[Filename of system command to run after screenshots] 54 | pb_sv_SsWidth 1024 //[Requested pixel width of remote screenshots] 55 | pb_sv_SsHeight 768 //[Requested pixel height of remote screenshots] 56 | pb_sv_SsXpct 50 //[Percentage across screen for remote screenshots] 57 | pb_sv_SsYpct 50 //[Percentage down screen for remote screenshots] 58 | pb_sv_SsSrate 2 //[Sample Rate for remote screenshots] 59 | pb_sv_SsDelay 3 //[Maximum delay client waits before capturing screenshot] 60 | pb_sv_SsPath "" //[Path where remote screenshots are saved] 61 | pb_sv_ssLogging 3 //[1=Reg. Log, 2=SS Log, 3=Both, 0=Neither] 62 | pb_sv_ssTimeout 300 //[Seconds] 63 | 64 | 65 | ; ------------------------------ 66 | ; Web Tool Settings 67 | ; ------------------------------ 68 | pb_sv_HttpPort 0 //[Port #] 69 | pb_sv_HttpAddr "" //[External IP Address] 70 | pb_sv_HttpKey "" //[Password] 71 | pb_sv_HttpRefresh 30 //[Seconds] 72 | pb_sv_HttpMaps "" //[Map list (separate by spaces)] 73 | pb_sv_HttpMapsPath "" //[Path where maps are loaded from in WebTool] 74 | pb_sv_HttpColText1 "FFFFFF" //[Text Color #1 in WebTool (default=FFFFFF)] 75 | pb_sv_HttpColText2 "0000FF" //[Text Color #2 in WebTool (default=0000FF)] 76 | pb_sv_HttpColBack1 "000000" //[Background Color #1 in WebTool (default=000000)] 77 | pb_sv_HttpColBack2 "808080" //[Background Color #2 in WebTool (default=808080)] 78 | pb_sv_HttpColLine1 "FF0000" //[Line Color #1 in WebTool (default=FF0000)] 79 | pb_sv_HttpColLine2 "0000FF" //[Line Color #2 in WebTool (default=0000FF)] 80 | pb_sv_HttpColMsg "FF0000" //[Message Color in WebTool (default=FF0000)] 81 | pb_sv_HttpShowGuid 1 //[0=No, 1=Yes (default=0)] 82 | 83 | 84 | ; ------------------------------ 85 | ; MD5 Tool Settings 86 | ; ------------------------------ 87 | pb_sv_md5toolfreq 100 // [Reducing this will increase the scan frequency that can cause lag and also increases the chances being kicked for Ignoring MD5 Tool Queries.] 88 | 89 | 90 | ; ------------------------------ 91 | ; Alias Settings (Only for supported games) 92 | ; ------------------------------ 93 | pb_sv_AliasFn "pbalias.dat" //[Filename (default="pbalias.dat")] 94 | pb_sv_AliasAutoLoad 0 //[0=No, 1=Yes (default=0)] 95 | pb_sv_AliasMax 10 //[Max # of Aliases to track for each PB GUID] 96 | pb_sv_AliasMaxEnforce 0 //[0=No, 1=Yes (default=0)] 97 | 98 | 99 | ; ------------------------------ 100 | ; PB Tasks (pb_sv_task [start delay (seconds)] [repeat (seconds)] [command]) 101 | ; ------------------------------ 102 | pb_sv_task 0 7200 pb_sv_ver // Keep-Alive for PBBans Hub (Do not remove. Will cause servers to appear as inactive when empty for long periods of time) 103 | pb_sv_task 0 86400 pb_sv_update // Check for PB updates daily (More dependable than UCON system) 104 | pb_sv_task 300 691200 pb_sv_md5toolempty // re-update MD5Tool list 105 | pb_sv_task 340 691200 pb_sv_load pbsvuser_mods_md5.cfg 106 | 107 | 108 | ; ------------------------------ 109 | ; Badname List (pb_sv_badname [grace_period_secs] [disallowed text]) 110 | ; ------------------------------ 111 | pb_sv_badnameempty 112 | 113 | 114 | ; ------------------------------ 115 | ; PB UCON Settings / Lists 116 | ; ------------------------------ 117 | pb_sv_usessionlimit 10 118 | pb_sv_ucontimeout 300 119 | pb_sv_uconempty 120 | pb_sv_uconadd 1 66.55.152.232 "pbbhub3-1" "pbbanshub" 121 | pb_sv_uconadd 1 66.55.152.233 "pbbhub3-2" "pbbanshub" 122 | pb_sv_uconadd 1 66.55.152.234 "pbbhub3-3" "pbbanshub" 123 | pb_sv_ProtectTag 1 GGC 124 | pb_sv_uconadd 1 31.214.160.253 "ggc" "bf2" 125 | 126 | 127 | ; ------------------------------ 128 | ; PB UCON Ignore List (Prevents UCON users from sending various commands) 129 | ; ------------------------------ 130 | pb_sv_uconignoreempty 131 | pb_sv_uconignore pb_sv_uconignoreempty 132 | pb_sv_uconignore quit 133 | pb_sv_uconignore pb_sv_md5toolfreq 134 | pb_sv_uconignore pb_sv_GuidRelax 135 | 136 | 137 | ; ------------------------------ 138 | ; Misc 139 | ; ------------------------------ 140 | pb_sv_writecfg pbucon.use // Enables UCON in case the server removes the file for any reason. 141 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbsv.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbsv.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/pb_amd-64/pbsvuser_mods_md5.cfg: -------------------------------------------------------------------------------- 1 | pb_sv_md5tool a "" v mods\bf2\Booster_client.zip SZ169667159 AT0 LEN2048 99D4DA6EA34CA44A6F77B746F61AF9BC 2 | pb_sv_md5tool a "" v mods\bf2\Common_client.zip SZ149828360 AT0 LEN2048 D7A9B566A56DE4DAC0629E9A2BD01E93 3 | pb_sv_md5tool a "" v mods\bf2\Fonts_client.zip SZ12284200 AT0 LEN2048 82DF5D46C2965616E23899185CBDDB12 4 | pb_sv_md5tool a "" v mods\bf2\Menu_client.zip SZ42196891 AT0 LEN2048 158F9C949BF01591C5079239FD871725 5 | pb_sv_md5tool a "" v mods\bf2\Objects_client.zip SZ641042098 AT0 LEN2048 691C0339D9BFDD885627F2A9EFECC058 6 | pb_sv_md5tool a "" v mods\bf2\Shaders_client.zip SZ224632 AT0 LEN2048 6D61C1FC1531A93B2E10C1632B23C350 7 | pb_sv_md5tool a "" v mods\bf2\ClientArchives.con SZ306 AT0 LEN256 3C80942192F21D95FE6FBA3907955DAB 8 | pb_sv_md5tool a "" v mods\bf2\GameLogicInit.con SZ13011 AT0 LEN2048 D9D74730755B6C83EBB2993B79692525 9 | pb_sv_md5tool a "" v mods\bf2\Init.con SZ301 AT0 LEN256 3872BE35FB5754D9ABB4A8D3FBF82066 10 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/bf2/rotate_demo.cfg: -------------------------------------------------------------------------------- 1 | # set the number of demo files to keep in rotation 2 | file_limit = 50 3 | 4 | # for local web server; edit this path and make it match the one in serversettings.con 5 | #target_root = /var/www/html 6 | target_root = /volume 7 | 8 | # set to 1 to enable ftp uploading 9 | use_ftp = 0 10 | 11 | # set to the target directory on the ftp server 12 | ftp_target_dir = /path/to/webroot/demos 13 | 14 | # login information for the ftp server goes here 15 | ftp_server = my.ftp.server 16 | ftp_user = my_user 17 | ftp_password = my_password 18 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | TMP='/home/bf2/tmp' 6 | 7 | INSTALLER="$TMP/bf2-linuxded-1.5.3153.0-installer.sh" 8 | INSTALLER_TGZ="$TMP/bf2-linuxded-1.5.3153.0-installer.tgz" 9 | BF2HUB_TGZ="$TMP/BF2Hub-Unranked-Linux-R3.tar.gz" 10 | MODMANAGER_ZIP="$TMP/ModManager-v2.2c.zip" 11 | MONOINSTALLER="$TMP/mono-1.1.12.1_0-installer.bin" 12 | BF2CCD_ZIP="$TMP/BF2CCD_1.4.2446.zip" 13 | 14 | # Get required packages 15 | dpkg --add-architecture i386 16 | apt -y update 17 | apt-get -y update 18 | apt-get -y install wget expect unzip libglib2.0-0:i386 19 | 20 | # Download missing assets 21 | while IFS=" " read url filename 22 | do 23 | args=(-nc -q --show-progress --progress=bar:force:noscroll) 24 | if [ -n "$filename" ]; then 25 | args+=(-O "$filename") 26 | fi 27 | wget "${args[@]}" "$url" 28 | done < assets.txt 29 | 30 | # Verify checksums 31 | if ! sha512sum -w -c assets.sha512; then 32 | echo 'Downloaded file checksum mismatch. Exiting.'; 33 | exit 1; 34 | fi 35 | 36 | # Extract server files from the installer 37 | tar -xvf $INSTALLER_TGZ -C $TMP 38 | chmod +x $INSTALLER ./extract 39 | ./extract 40 | 41 | # Move BF2Hub files into server directory 42 | tar -xvf $BF2HUB_TGZ -C "$TMP/srv" 43 | 44 | # Move ModManager files into server directory 45 | unzip $MODMANAGER_ZIP -d "$TMP/srv" 46 | 47 | # Install Mono 48 | chmod +x $MONOINSTALLER 49 | $MONOINSTALLER 50 | 51 | # Move mono into the server folder so it can be copied to the runtime image 52 | mv /opt/mono-1.1.12.1/ "$TMP/srv/" 53 | 54 | # Move BF2CC Daemon into server directory 55 | unzip $BF2CCD_ZIP -d "$TMP/srv/bf2ccd" 56 | 57 | # Clean up unused folders (we have updated pb) 58 | rm -r $TMP/srv/pb_* $TMP/srv/bin/ia-32 59 | 60 | # Replace with our own BF2 server files (custom settings and scripts) 61 | cp -r "$TMP/bf2/." "$TMP/srv" 62 | 63 | # Create empty server folder to copy our files into if it's empty on the host system 64 | mkdir -p $VOLUME 65 | chmod -R 700 $VOLUME/ 66 | 67 | exit 0 68 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/build/extract: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect 2 | 3 | set timeout -1 4 | spawn "./bf2-linuxded-1.5.3153.0-installer.sh" --keep --target ./srv/ 5 | 6 | expect { 7 | eof { send_user "\nunexpected eof in extraction\n"; exit 1 } 8 | "*ress return" 9 | } 10 | 11 | send "^c" 12 | 13 | send_user "\nExtraction finished\n" -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/runtime/nginx/default: -------------------------------------------------------------------------------- 1 | # Default nginx server configuration 2 | # 3 | server { 4 | listen 80 default_server; 5 | listen [::]:80 default_server; 6 | 7 | # root /var/www/html; 8 | root /volume/www; 9 | 10 | # Add index.php to the list if you are using PHP 11 | index index.html index.htm index.nginx-debian.html; 12 | 13 | server_name _; 14 | 15 | location /demos/ { 16 | # First attempt to serve request as file, then 17 | # as directory, then fall back to displaying a 404. 18 | alias /volume/demos/uploaded/; 19 | try_files $uri $uri/ =404; 20 | autoindex on; 21 | autoindex_format json; 22 | } 23 | 24 | location ~ ([^\/]+\.bf2demo)$ { 25 | alias /volume/demos/uploaded/$1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/runtime/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | TMP='/home/bf2/tmp' 5 | SRV='/home/bf2/srv' 6 | VOLUME='/volume' 7 | 8 | generate_pw() { 9 | echo "$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c10)" 10 | } 11 | 12 | replace_var() { 13 | echo "$3: $1 => [$2]" 14 | replaceEscaped=$(echo "$2" | sed 's/[&/\]/\\&/g') 15 | sed -i --follow-symlinks -e "s/$1/$replaceEscaped/g" $3 16 | } 17 | 18 | # Check if target volume is empty 19 | if [ "$(ls -A $SRV)" ]; then 20 | echo "$SRV is not empty. Skipping..." 21 | else 22 | # Move server files to persisted folder (-n without overwriting) 23 | echo "$SRV is empty. Moving server files..." 24 | mv -n $TMP/srv/* $SRV/ 25 | 26 | # Set server settings from environment variables 27 | rcon_pw="${ENV_RCON_PASSWORD:-"$(generate_pw)"}" 28 | bf2ccd_pw="${ENV_BF2CCD_PASSWORD:-"$(generate_pw)"}" 29 | bf2ccd_pw_md5="$(echo -n $bf2ccd_pw | md5sum | tr a-z A-Z | tr -d - | xargs echo -n)" 30 | replace_var '{{server_name}}' "${ENV_SERVER_NAME:-"bf2-docker"}" "$SRV/mods/bf2/settings/serversettings.con" 31 | replace_var '{{max_players}}' "${ENV_MAX_PLAYERS:-"16"}" "$SRV/mods/bf2/settings/serversettings.con" 32 | replace_var '{{server_port}}' "${ENV_SERVER_PORT:-"16567"}" "$SRV/mods/bf2/settings/serversettings.con" 33 | replace_var '{{server_port}}' "${ENV_SERVER_PORT:-"16567"}" "$SRV/bf2ccd/default.profile" 34 | replace_var '{{gamespy_port}}' "${ENV_GAMESPY_PORT:-"29900"}" "$SRV/mods/bf2/settings/serversettings.con" 35 | replace_var '{{gamespy_port}}' "${ENV_GAMESPY_PORT:-"29900"}" "$SRV/bf2ccd/default.profile" 36 | replace_var '{{demos_url}}' "${ENV_DEMOS_URL:-"http://example.com/demos/"}" "$SRV/mods/bf2/settings/serversettings.con" 37 | replace_var '{{rcon_password}}' "$rcon_pw" "$SRV/mods/bf2/settings/modmanager.con" 38 | replace_var '{{rcon_password}}' "$rcon_pw" "$SRV/bf2ccd/default.profile" 39 | replace_var '{{bf2ccd_password}}' "$bf2ccd_pw_md5" "$SRV/bf2ccd/users.xml" 40 | 41 | # Create volume directory for all persisted changes 42 | echo 'Moving persisted data and creating symlinks...' 43 | mkdir -m 777 -p $VOLUME 44 | mkdir -m 777 -p $VOLUME/svlogs 45 | mkdir -m 777 -p $VOLUME/svss 46 | mkdir -m 777 -p $VOLUME/demos 47 | mkdir -m 777 -p $VOLUME/demos/pending 48 | mkdir -m 777 -p $VOLUME/demos/uploaded 49 | mkdir -m 777 -p $VOLUME/www 50 | install -m 777 /dev/null $VOLUME/bf2.log 51 | install -m 777 /dev/null $VOLUME/modmanager.log 52 | install -m 777 /dev/null $VOLUME/pbalias.dat 53 | install -m 777 /dev/null $VOLUME/sv_viol.log 54 | mv -n $SRV/mods/bf2/settings $VOLUME 55 | chmod -R 777 $VOLUME/settings 56 | rm -rf $SRV/mods/bf2/demos 57 | rm -rf $SRV/pb_amd-64/svss 58 | rm -rf $SRV/pb_amd-64/svlogs 59 | ln -sf $VOLUME/settings $SRV/mods/bf2/settings 60 | ln -sf $VOLUME/demos/pending $SRV/mods/bf2/demos 61 | ln -sf $VOLUME/svss $SRV/pb_amd-64/svss 62 | ln -sf $VOLUME/svlogs $SRV/pb_amd-64/svlogs 63 | ln -sf $VOLUME/bf2.log $SRV/bf2.log 64 | ln -sf $VOLUME/modmanager.log $SRV/modmanager.log 65 | ln -sf $VOLUME/pbalias.dat $SRV/pbalias.dat 66 | ln -sf $VOLUME/sv_viol.log $SRV/pb_amd-64/sv_viol.log 67 | mv -n $SRV/bf2ccd $VOLUME 68 | chmod -R 777 $VOLUME/bf2ccd 69 | ln -sf $VOLUME/bf2ccd $SRV/bf2ccd 70 | 71 | # Set execute permissions 72 | echo 'Setting execute permissions...' 73 | cd $SRV 74 | chmod +x ./start_bf2hub.sh ./bin/amd-64/bf2 ./mono-1.1.12.1/bin/mono ./bf2ccd/bf2ccd.exe 75 | chmod -R 777 ./pb_amd-64 76 | chmod -R 777 ./bf2ccd 77 | chmod -R 777 . # temp D: 78 | fi 79 | 80 | # Start nginx for demos 81 | service nginx start 82 | 83 | # Start BF2CC Daemon 84 | echo 'Starting BF2CC Daemon...' 85 | export TERM=xterm 86 | su -c "cd $SRV && ./mono-1.1.12.1/bin/mono ./bf2ccd/bf2ccd.exe -noquitprompts -autostart >/dev/null" - bf2 87 | 88 | exit 0 89 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/runtime/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | WWW='/var/www/html' 6 | DEMOS="$WWW/demos" 7 | TMP='/home/bf2/tmp' 8 | 9 | # Get required packages and create our user 10 | # libncurses5 = run bf2 11 | # python = rotate_demo.py 12 | # nginx = host demos 13 | # libglib2.0-0:i386 = run BF2CC Daemon 14 | dpkg --add-architecture i386 15 | apt -y update 16 | apt-get -y update 17 | apt-get -y install libncurses5 python nginx libglib2.0-0:i386 18 | apt-get clean 19 | rm -rf /var/lib/apt/lists/* 20 | useradd --create-home --shell /bin/bash bf2 21 | 22 | # Replace nginx settings 23 | mv "$TMP/nginx/default" '/etc/nginx/sites-available/' 24 | 25 | # Delete temp files, but not the temp server directory to move during start 26 | find $TMP/* -maxdepth 0 -type d,f -not -name 'srv' -not -name 'run.sh' -exec rm -r "{}" \; 27 | 28 | # Create empty server folder to copy our files into if it's empty on the host system 29 | mkdir -p $VOLUME 30 | chmod -R 700 $VOLUME/ 31 | 32 | # Create demos web folder 33 | mkdir -p $DEMOS 34 | chmod -R 777 $DEMOS/ 35 | 36 | # Change owner 37 | chown -R bf2:bf2 /home/bf2/ 38 | chmod -R 700 /home/bf2/ 39 | 40 | exit 0 41 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-bf2cc/assets/runtime/www/index.html: -------------------------------------------------------------------------------- 1 | :) -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .vscode 3 | build.bat 4 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM debian:bullseye-slim AS build 3 | 4 | # Add assets to image 5 | WORKDIR /home/bf2/tmp 6 | COPY ./assets/build ./ 7 | 8 | # Download and extract server files 9 | RUN bash -x ./build.sh 10 | 11 | # Runtime stage 12 | FROM debian:bullseye-slim AS runtime 13 | WORKDIR /home/bf2/tmp 14 | LABEL maintainer=nihlen 15 | 16 | # Environment variables 17 | ENV SERVER_NAME="bf2-docker" 18 | 19 | # Copy runtime assets 20 | COPY ./assets/runtime ./ 21 | 22 | # Install required packages and set permissions 23 | RUN bash -x ./setup.sh 24 | 25 | # Copy server files from the build stage 26 | COPY --from=build /home/bf2/tmp/srv ./srv 27 | 28 | # Move server files to persisted folder and start server 29 | CMD ./run.sh 30 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/assets.sha512: -------------------------------------------------------------------------------- 1 | b807684116a0f3d2590390567a5a0da8fa9b0804fb9229ae056fa4cad2d8a46cd306d39592a04167c8b757083e4d20a1d28fe04154c25dab3156ef8be9db3702 bf2-linuxded-1.5.3153.0-installer.tgz 2 | 8391cac06f6667ad4cb495e5ed907159b67ac7c9cb5a1d5fa337d363d95d378767382326add388d06c511653a6295241f64eceffc4648b524cede89e0479a84d BF2Hub-Unranked-Linux-R3.tar.gz 3 | d025aff1a6713da0381b8844f64cd016a66ab907bc936e74ca31ef0781424c02597116c322044da46877766318f28c4c4822e5c4bdec1c19a90157c39fe5bbb4 ModManager-v2.2c.zip 4 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/assets.txt: -------------------------------------------------------------------------------- 1 | https://www.bf-games.net/downloads/mirror/2956 bf2-linuxded-1.5.3153.0-installer.tgz 2 | https://www.bf2hub.com/downloads/BF2Hub-Unranked-Linux-R3.tar.gz 3 | https://static.nihlen.net/bf2/server/ModManager-v2.2c.zip 4 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/admin/modules/mm_webadmin.py: -------------------------------------------------------------------------------- 1 | """ 2 | WebAdmin Module 3 | ====================== 4 | Send and receive messages from a master server to extend functionality 5 | of the Battlefield 2 server and simplify external communication. 6 | 7 | This base module registers the game event handlers, timers and sets up a 8 | socket connection server 9 | 10 | ======= Author ======= 11 | https://bf2.nihlen.net 12 | 13 | ====== Based on ====== 14 | mm_sample.py by Steven 'Killing' Hartland 15 | bf2bot.py by DeadEd 16 | """ 17 | 18 | import bf2 19 | import host 20 | import socket 21 | import math 22 | import re 23 | import errno 24 | import mm_utils 25 | # import mm_rcon 26 | from bf2.stats.constants import * 27 | 28 | # 29 | # Module information 30 | # 31 | __version__ = 0.5 32 | __description__ = "WebAdmin v%s" % __version__ 33 | __required_modules__ = {'modmanager': 1.6} 34 | __supports_reload__ = True # Does this module support reload (are all its reference closed on shutdown?) 35 | __supported_games__ = {'bf2': True, 'bf2142': False} 36 | 37 | configDefaults = { 38 | # 39 | # Web Admin settings 40 | # 41 | 'serverHost': 'host.docker.internal', # master server host 42 | 'serverPort': 4300, # master server port 43 | 'timerInterval': 0.25, # fast timer interval in seconds 44 | } 45 | 46 | # 47 | # Debug output in the server console 48 | # 49 | IS_DEBUG = False 50 | 51 | # 52 | # Ticket timer 53 | # 54 | TICKET_STATUS_TIMER = None # Timer object 55 | TICKET_STATUS_DELAY = 60 # Timer delay 56 | 57 | # 58 | # WebAdmin Class 59 | # 60 | 61 | 62 | class WebAdmin(object): 63 | 64 | def __init__(self, modManager): 65 | """Provides static initialisation.""" 66 | 67 | self.mm = modManager 68 | self.__state = 0 69 | 70 | # Custom RCon Commands 71 | self.__cmds = { 72 | 'connect': {'method': self.cmdConnect, 'args': ' ', 'level': 10}, 73 | 'connectprivate': {'method': self.cmdConnectPrivate, 'args': '', 'level': 10} 74 | } 75 | 76 | # Timers 77 | self.fastTimer = None 78 | 79 | # Web Admin socket 80 | self.__socket = None 81 | 82 | # We need updates to receive socket messages 83 | self.mm.registerUpdates(self.update) 84 | 85 | # All initialisation done 86 | self.mm.info("ModManager WebAdmin started") 87 | 88 | def openSocket(self): 89 | """Connect to Server TCP socket.""" 90 | if self.__socket: 91 | self.closeSocket() 92 | 93 | try: 94 | # If a docker container name or hostname is given then we need to resolve it to an IPv4 address first 95 | if not self.valid_ipv4(self.__config['serverHost']): 96 | resolvedIp = socket.gethostbyname(self.__config['serverHost']) 97 | self.mm.info("Resolved host name %s to %s" % (self.__config['serverHost'], resolvedIp)) 98 | self.__config['serverHost'] = resolvedIp 99 | 100 | self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 101 | self.__socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 102 | self.__socket.settimeout(5) 103 | self.__socket.connect((self.__config['serverHost'], self.__config['serverPort'])) 104 | 105 | # Set non-blocking last, else it will freeze when connecting 106 | self.__socket.setblocking(0) 107 | 108 | self.outbuffer = OutputBuffer(self.mm, self.__socket, True) # outgoing buffer 109 | self.inbuffer = '' # incoming buffer 110 | 111 | self.mm.info("WebAdmin successfully connected to server socket %s:%s" % (self.__config['serverHost'], self.__config['serverPort'])) 112 | 113 | self.onSocketConnected() 114 | 115 | except Exception, detail: 116 | self.mm.error("WebAdmin failed to connect to server socket %s:%s (%s)" % (self.__config['serverHost'], self.__config['serverPort'], detail), False) 117 | self.__socket = None 118 | 119 | def closeSocket(self): 120 | if self.__socket: 121 | self.__socket.close() 122 | self.__socket = None 123 | self.mm.info("WebAdmin socket closed") 124 | self.onSocketDisconnected() 125 | 126 | def sendMessage(self, message): 127 | # End with \n so readLine() works on the server, else all messages will stack up until newline is received 128 | if self.outbuffer: 129 | self.outbuffer.enqueue(message + "\n") 130 | 131 | def sendEventMessage(self, type, *args): 132 | self.sendMessage("%s\t%s" % (type, '\t'.join(map(str, args)))) 133 | 134 | def sendReconnectMessage(self): 135 | # self.onGameStatusChange(self.currentGameStatus) 136 | self.onGameStatusChange(self.mm.currentGameStatus) 137 | for p in bf2.playerManager.getPlayers(): 138 | self.onPlayerConnect(p) 139 | self.onPlayerScore(p, 0) 140 | vehicle = p.getVehicle() 141 | if vehicle: 142 | self.onEnterVehicle(p, vehicle) 143 | 144 | def recvMessage(self, message): 145 | # Only add things that can't be achieved through host.rcon_invoke 146 | try: 147 | self.handleCommand(message) 148 | except Exception, detail: 149 | self.mm.error("Handle command error: (%s)" % detail, False) 150 | 151 | def handleCommand(self, message): 152 | """Handle a command""" 153 | 154 | # Run RCon command 155 | if message.startswith("rcon "): 156 | 157 | rcon = message[len("rcon "):] 158 | response = host.rcon_invoke(rcon) 159 | if response[-1] == '\n': 160 | response = response[:-1] 161 | self.sendMessage(response.replace('\n', '\b')) # Use something else than backspace? \t is used by events 162 | 163 | # Rcon command and return with a response code 164 | elif message.startswith("rconresponse "): 165 | 166 | (code, rcon) = mm_utils.largs(message[len("rconresponse "):], None, 2, '') 167 | response = host.rcon_invoke(rcon) 168 | if response[-1] == '\n': 169 | response = response[:-1] 170 | self.sendMessage("response\t%s\t%s" % (code, response.replace('\n', '\b'))) # Use something else than backspace? \t is used by events 171 | 172 | # PM a player using the rcon feedback function (message appears in their console) 173 | elif message.startswith("pm "): 174 | (playerid, message) = mm_utils.largs(message[len("pm "):], None, 2, '') 175 | p = mm_utils.find_player(playerid) 176 | if p: 177 | host.rcon_feedback(p.index, str(message)) 178 | 179 | # Teleport a player 180 | elif message.startswith("position "): 181 | 182 | (playerid, x, h, y) = mm_utils.largs(message[len("position "):], None, 4, '') 183 | p = mm_utils.find_player(playerid) 184 | pos = (float(x), float(h), float(y)) 185 | if p: 186 | setPlayerPosition(p, pos) 187 | 188 | # Rotate a player 189 | elif message.startswith("rotation "): 190 | 191 | (playerid, yaw, pitch, roll) = mm_utils.largs(message[len("rotation "):], None, 4, '') 192 | p = mm_utils.find_player(playerid) 193 | rot = (float(yaw), float(pitch), float(roll)) 194 | if p: 195 | setPlayerRotation(p, rot) 196 | 197 | # Set player health (vehicle damage) 198 | elif message.startswith("damage "): 199 | 200 | (playerid, damage) = mm_utils.largs(message[len("damage "):], None, 2, '') 201 | p = mm_utils.find_player(playerid) 202 | if p: 203 | setPlayerDamage(p, get_int((damage))) 204 | 205 | # Set player rank 206 | elif message.startswith("rank "): 207 | 208 | (playerid, ranknum, rankevent) = mm_utils.largs(message[len("rank "):], None, 3, '') 209 | p = mm_utils.find_player(playerid) 210 | ranknum = get_int(ranknum) 211 | rankevent = rankevent == '1' 212 | if p: 213 | setPlayerRank(p, ranknum, rankevent) 214 | 215 | # Give player a medal award 216 | elif message.startswith("medal "): 217 | 218 | (playerid, medalnum, medalval) = mm_utils.largs(message[len("medal "):], None, 3, '') 219 | p = mm_utils.find_player(playerid) 220 | if p and medalnum and medalval: 221 | awardPlayer(p, get_int(medalnum), get_int(medalval)) 222 | 223 | # Send game event 224 | elif message.startswith("gameevent "): 225 | 226 | (playerid, event, data) = mm_utils.largs(message[len("gameevent "):], None, 3, '') 227 | p = mm_utils.find_player(playerid) 228 | event = get_int(event) 229 | data = get_int(data) 230 | if p and event and data: 231 | bf2.gameLogic.sendGameEvent(p, event, data) 232 | 233 | # Send HUD event 234 | elif message.startswith("hudevent "): 235 | 236 | (playerid, event, data) = mm_utils.largs(message[len("hudevent "):], None, 3, '') 237 | p = mm_utils.find_player(playerid) 238 | event = get_int(event) 239 | data = get_int(data) 240 | if p and event and data: 241 | bf2.gameLogic.sendHudEvent(p, event, data) 242 | 243 | # Set score 244 | elif message.startswith("score "): 245 | 246 | (playerid, totalScore, teamScore, kills, deaths) = mm_utils.largs(message[len("score "):], None, 5, '') 247 | p = mm_utils.find_player(playerid) 248 | if p: 249 | p.score.rplScore = int(teamScore) 250 | p.score.kills = int(kills) 251 | p.score.deaths = int(deaths) 252 | p.score.score = int(totalScore) 253 | 254 | # Set team 255 | elif message.startswith("team "): 256 | 257 | (playerid, teamid) = mm_utils.largs(message[len("team "):], None, 2, '') 258 | p = mm_utils.find_player(playerid) 259 | if p: 260 | p.setTeam(get_int(teamid)) 261 | if p.isAlive(): 262 | setPlayerDamage(p, 1) 263 | self.onPlayerChangeTeams(p, False) 264 | 265 | # Set timer interval 266 | elif message.startswith("timerinterval "): 267 | 268 | interval = message[len("timerinterval "):] 269 | self.__config['timerInterval'] = float(interval) 270 | self.stopFastTimer() 271 | self.startFastTimer(float(self.__config['timerInterval']), (1, 2, 3)) 272 | 273 | def cmdExec(self, ctx, cmd): 274 | """Execute a WebAdmin sub command.""" 275 | return mm_utils.exec_subcmd(self.mm, self.__cmds, ctx, cmd) 276 | 277 | def cmdConnect(self, ctx, cmd): 278 | global IS_DEBUG 279 | 280 | (ip, port) = mm_utils.largs(cmd, None, 2, '') 281 | 282 | self.__config['serverHost'] = ip 283 | self.__config['serverPort'] = get_int(port) 284 | IS_DEBUG = self.__config['serverHost'] == '127.0.0.1' 285 | 286 | # Possible reconnection 287 | self.openSocket() 288 | 289 | if self.__socket: 290 | # Send data that the server missed, like player connections, vehicles and stats 291 | self.sendReconnectMessage() 292 | ctx.write('Connected successfully to %s:%s' % (ip, port)) 293 | else: 294 | ctx.write('Connection failed to %s:%s' % (ip, port)) 295 | 296 | return 1 297 | 298 | def cmdConnectPrivate(self, ctx, cmd): 299 | global IS_DEBUG 300 | 301 | ip = ctx.conn.addr[0] 302 | port = cmd 303 | self.__config['serverHost'] = ip 304 | self.__config['serverPort'] = get_int(port) 305 | IS_DEBUG = self.__config['serverHost'] == '127.0.0.1' 306 | 307 | # Possible reconnection 308 | self.openSocket() 309 | 310 | if self.__socket: 311 | # Send data that the server missed, like player connections, vehicles and stats 312 | self.sendReconnectMessage() 313 | ctx.write('Connected successfully to %s:%s' % (ip, port)) 314 | else: 315 | ctx.write('Connection failed to %s:%s' % (ip, port)) 316 | 317 | return 1 318 | 319 | def valid_ipv4(self, s): 320 | a = s.split('.') 321 | if len(a) != 4: 322 | return False 323 | for x in a: 324 | if not x.isdigit(): 325 | return False 326 | i = int(x) 327 | if i < 0 or i > 255: 328 | return False 329 | return True 330 | 331 | def shutdown(self): 332 | """Shutdown and stop processing.""" 333 | 334 | # Unregister game handlers and do any other 335 | # other actions to ensure your module no longer affects 336 | # the game in anyway 337 | self.mm.unregisterRconCmdHandler('connect') 338 | 339 | self.closeSocket() 340 | self.mm.info("ModManager WebAdmin shutdown") 341 | 342 | # Flag as shutdown as there is currently way to: 343 | # host.unregisterHandler 344 | self.__state = 2 345 | 346 | def update(self): 347 | # From mm_rcon.AdminConnection.update 348 | if 1 != self.__state: 349 | return 0 350 | 351 | if not self.__socket: 352 | # socket already closed e.g. DOS protection 353 | return 0 354 | 355 | # Process incoming requests 356 | err = None 357 | try: 358 | while not err: 359 | data = self.__socket.recv(1024) 360 | if data: 361 | self.inbuffer += data 362 | while not err: 363 | nlpos = self.inbuffer.find('\n') 364 | if nlpos != -1: 365 | self.recvMessage(self.inbuffer[0:nlpos]) 366 | self.inbuffer = self.inbuffer[nlpos+1:] # keep rest of buffer 367 | else: 368 | if len(self.inbuffer) > 128: 369 | err = 'data format error: no newline in message' 370 | break 371 | else: 372 | err = 'peer disconnected' 373 | self.onSocketDisconnected() 374 | 375 | if not self.__socket: 376 | # socket already closed e.g. DOS protection 377 | return 0 378 | 379 | except socket.error, detail: 380 | if detail[0] != errno.EWOULDBLOCK: 381 | err = detail[1] 382 | if detail[0] != errno.EPIPE and detail[0] != errno.ECONNRESET: 383 | # only print error if the client didnt disconnect 384 | self.mm.error("webadmin: update failed %s" % detail) 385 | 386 | if not err: 387 | # Send any output 388 | err = self.outbuffer.update() 389 | 390 | if err: 391 | self.mm.error("ERROR: webadmin update: %s" % err) 392 | self.closeSocket() 393 | #self.close( err ) 394 | return 0 395 | 396 | return 1 397 | 398 | def init(self): 399 | """Provides default initialisation.""" 400 | global TICKET_STATUS_TIMER, TICKET_STATUS_DELAY 401 | 402 | # Load the configuration 403 | self.__config = self.mm.getModuleConfig(configDefaults) 404 | 405 | # Open Web Admin socket 406 | self.openSocket() 407 | 408 | # Register game handlers and do dynamic initialisation 409 | if self.__state == 0: 410 | 411 | # Game Status Events 412 | host.registerGameStatusHandler(self.onGameStatusChange) 413 | 414 | # Game Events 415 | host.registerHandler('ControlPointChangedOwner', self.onControlPointChangedOwner, 1) 416 | 417 | # Ticket timer (Don't care atm, move timers to server?) 418 | #TICKET_STATUS_TIMER = bf2.Timer(self.onTicketStatusTimer, TICKET_STATUS_DELAY, 1) 419 | # TICKET_STATUS_TIMER.setRecurring(TICKET_STATUS_DELAY) 420 | 421 | # Player Events 422 | host.registerHandler('PlayerConnect', self.onPlayerConnect, 1) 423 | host.registerHandler('PlayerSpawn', self.onPlayerSpawn, 1) 424 | host.registerHandler('PlayerScore', self.onPlayerScore, 1) 425 | host.registerHandler('PlayerChangeTeams', self.onPlayerChangeTeams, 1) 426 | host.registerHandler('PlayerRevived', self.onPlayerRevived, 1) 427 | host.registerHandler('PlayerKilled', self.onPlayerKilled, 1) 428 | host.registerHandler('PlayerDeath', self.onPlayerDeath, 1) 429 | host.registerHandler('PlayerDisconnect', self.onPlayerDisconnect, 1) 430 | 431 | # Vehicle Events 432 | host.registerHandler('EnterVehicle', self.onEnterVehicle, 1) 433 | host.registerHandler('ExitVehicle', self.onExitVehicle, 1) 434 | host.registerHandler('VehicleDestroyed', self.onVehicleDestroyed, 1) 435 | 436 | # Misc Events 437 | host.registerHandler('ChatMessage', self.onChatMessage, 1) 438 | 439 | # self.sendMessage("WebAdmin initialised on the BF2 server.") 440 | 441 | # Register our rcon command handlers 442 | self.mm.registerRconCmdHandler('wa', {'method': self.cmdExec, 'subcmds': self.__cmds, 'level': 1}) 443 | 444 | # Update to the running state 445 | self.__state = 1 446 | 447 | def startFastTimer(self, interval, data): 448 | if self.__socket and (self.fastTimer is None) and (len(bf2.playerManager.getPlayers()) > 0): 449 | self.stopFastTimer() 450 | self.fastTimer = bf2.Timer(self.onFastTimer, interval, 1, data) 451 | self.fastTimer.setRecurring(interval) 452 | 453 | def stopFastTimer(self): 454 | if self.fastTimer: 455 | self.fastTimer.destroy() 456 | self.fastTimer = None 457 | 458 | # 459 | # Socket Events 460 | # 461 | def onSocketConnected(self): 462 | servername = host.rcon_invoke('sv.serverName').strip() 463 | gameport = host.rcon_invoke('sv.serverPort').strip() 464 | queryport = host.rcon_invoke('sv.gameSpyPort').strip() 465 | maplist = ",".join(getMapList()) 466 | self.sendEventMessage("serverInfo", servername, maplist, gameport, queryport, host.ss_getParam('maxPlayers')) 467 | 468 | def onSocketDisconnected(self): 469 | self.stopFastTimer() 470 | 471 | # 472 | # Game Status Events 473 | # 474 | def onGameStatusChange(self, statusChange): 475 | if self.__state != 1: 476 | return 0 477 | 478 | #self.currentGameStatus = statusChange 479 | if statusChange == bf2.GameStatus.Playing: 480 | self.sendEventMessage("gameStatePlaying", bf2.gameLogic.getTeamName(1), bf2.gameLogic.getTeamName(2), host.sgl_getMapName(), host.ss_getParam('maxPlayers')) 481 | self.startFastTimer(float(self.__config['timerInterval']), (1, 2, 3)) 482 | 483 | elif statusChange == bf2.GameStatus.EndGame: 484 | self.sendEventMessage("gameStateEndGame", bf2.gameLogic.getTeamName(1), host.sgl_getParam('tickets', 1, 0), 485 | bf2.gameLogic.getTeamName(2), host.sgl_getParam('tickets', 2, 0), host.sgl_getMapName()) 486 | elif statusChange == bf2.GameStatus.PreGame: 487 | self.sendEventMessage("gameStatePreGame") 488 | elif statusChange == bf2.GameStatus.Paused: 489 | self.sendEventMessage("gameStatePaused") 490 | elif statusChange == bf2.GameStatus.RestartServer: 491 | self.sendEventMessage("gameStateRestart") 492 | elif statusChange == bf2.GameStatus.NotConnected: 493 | self.sendEventMessage("gameStateNotConnected") 494 | else: 495 | host.rcon_invoke('echo "unknown status: ' + str(statusChange) + '"') 496 | 497 | # 498 | # Game Events 499 | # 500 | def onControlPointChangedOwner(self, controlPoint, underAttack): 501 | if 1 != self.__state: 502 | return 0 503 | flagPos = controlPoint.cp_getParam('flag') 504 | owner = controlPoint.cp_getParam('team') 505 | if flagPos == 1 and underAttack == 1 and owner == 0: 506 | self.sendEventMessage("controlPointCapture", 1, controlPoint.templateName) 507 | elif flagPos == 2 and underAttack == 1 and owner == 0: 508 | self.sendEventMessage("controlPointCapture", 2, controlPoint.templateName) 509 | elif underAttack == 0 and owner == flagPos: 510 | self.sendEventMessage("controlPointNeutralised", controlPoint.templateName) 511 | 512 | # 513 | # Timer Events 514 | # 515 | def onTicketStatusTimer(self, data): 516 | if 1 != self.__state: 517 | return 0 518 | if host.pmgr_getNumberOfPlayers() > 0: 519 | self.sendEventMessage("ticketStatus", bf2.gameLogic.getTeamName(1), host.sgl_getParam('tickets', 1, 0), 520 | bf2.gameLogic.getTeamName(2), host.sgl_getParam('tickets', 2, 0), host.sgl_getMapName()) 521 | 522 | def onFastTimer(self, data): 523 | try: 524 | # Players 525 | for player in bf2.playerManager.getPlayers(): 526 | self.onPlayerUpdate(player) 527 | 528 | # Projectiles 529 | allProjs = bf2.objectManager.getObjectsOfType('dice.hfe.world.ObjectTemplate.GenericProjectile') 530 | if len(allProjs) > 0: 531 | for proj in allProjs: 532 | if proj.templateName == 'agm114_hellfire_tv': 533 | self.onProjectileUpdate(proj) 534 | 535 | except Exception, detail: 536 | self.sendMessage('ERROR: %s' % detail) 537 | host.rcon_invoke('echo "%s"' % detail) 538 | 539 | def onPlayerUpdate(self, player): 540 | # return 541 | vehicle = player.getVehicle() 542 | self.sendEventMessage("playerPositionUpdate", player.index, getPositionString(vehicle), getRotationString(vehicle), player.getPing()) 543 | 544 | def onProjectileUpdate(self, projectile): 545 | self.sendEventMessage("projectilePositionUpdate", findId(projectile), projectile.templateName, getPositionString(projectile), getRotationString(projectile)) 546 | if IS_DEBUG: 547 | host.rcon_invoke('game.sayall "Proj: %s"' % getRotationString(projectile)) 548 | 549 | # 550 | # Player Events 551 | # 552 | def onPlayerConnect(self, player): 553 | if 1 != self.__state: 554 | return 0 555 | self.sendEventMessage("playerConnect", player.index, player.getName(), player.getProfileId(), player.getAddress(), mm_utils.get_cd_key_hash(player), player.getTeam()) 556 | self.startFastTimer(float(self.__config['timerInterval']), (1, 2, 3)) 557 | 558 | def onPlayerSpawn(self, player, soldier): 559 | if 1 != self.__state: 560 | return 0 561 | self.sendEventMessage("playerSpawn", player.index, getPositionString(soldier), getRotationString(soldier)) 562 | 563 | def onPlayerChangeTeams(self, player, humanHasSpawned): 564 | if 1 != self.__state: 565 | return 0 566 | self.sendEventMessage("playerChangeTeam", player.index, player.getTeam()) 567 | 568 | def onPlayerScore(self, player, difference): 569 | if 1 != self.__state: 570 | return 0 571 | #self.sendEventMessage("playerScore", player.index, difference) 572 | self.sendEventMessage("playerScore", player.index, player.score.score, player.score.rplScore, player.score.kills, player.score.deaths) 573 | 574 | def onPlayerRevived(self, revivee, medic): 575 | if 1 != self.__state: 576 | return 0 577 | self.sendEventMessage("playerRevived", medic.index, revivee.index) 578 | 579 | # TODO: crashing in plane/heli doesn't show -DeadEd 580 | def onPlayerKilled(self, victim, attacker, weapon, assists, victimSoldierObject): 581 | if 1 != self.__state: 582 | return 0 583 | if victim.index == attacker.index: 584 | self.sendEventMessage("playerKilledSelf", victim.index, getPositionString(victimSoldierObject), ) 585 | elif victim.getTeam() == attacker.getTeam(): 586 | self.sendEventMessage("playerTeamkilled", attacker.index, getPositionString(attacker.getVehicle()), victim.index, getPositionString(victimSoldierObject), ) 587 | elif attacker == None and weapon == None and victimSoldierObject != None: 588 | # TODO: being run over by a vehicle doesn't show -DeadEd 589 | if hasattr(attacker, 'lastDrivingPlayerIndex'): 590 | attacker = bf2.playerManager.getPlayerByIndex(victimSoldierObject.lastDrivingPlayerIndex) 591 | self.sendEventMessage("playerKilled", attacker.index, getPositionString(attacker.getVehicle()), victim.index, getPositionString(victimSoldierObject), "roadkill") 592 | else: 593 | self.sendEventMessage("playerKilled", attacker.index, getPositionString(attacker.getVehicle()), victim.index, getPositionString(victimSoldierObject), weapon.templateName) 594 | 595 | def onPlayerDeath(self, player, soldierObject): 596 | if 1 != self.__state: 597 | return 0 598 | self.sendEventMessage("playerDeath", player.index, getPositionString(soldierObject)) 599 | 600 | def onPlayerDisconnect(self, player): 601 | if 1 != self.__state: 602 | return 0 603 | self.sendEventMessage("playerDisconnect", player.index) 604 | if (len(bf2.playerManager.getPlayers()) < 1): 605 | self.stopFastTimer() 606 | host.rcon_invoke('echo "Players: %s" ' % str(len(bf2.playerManager.getPlayers()))) 607 | 608 | # 609 | # Vehicle Events 610 | # 611 | def onEnterVehicle(self, player, vehicle, freeSoldier=False): 612 | if 1 != self.__state: 613 | return 0 614 | rootVehicle = bf2.objectManager.getRootParent(vehicle) 615 | self.sendEventMessage("enterVehicle", player.index, findId(rootVehicle), rootVehicle.templateName, vehicle.templateName) 616 | 617 | def onExitVehicle(self, player, vehicle): 618 | if 1 != self.__state: 619 | return 0 620 | self.sendEventMessage("exitVehicle", player.index, -1, "Unknown", vehicle.templateName) 621 | 622 | def onVehicleDestroyed(self, vehicle, attacker): 623 | if 1 != self.__state: 624 | return 0 625 | self.sendEventMessage("vehicleDestroyed", -1, vehicle.templateName) 626 | 627 | # 628 | # Chat Events 629 | # 630 | def onChatMessage(self, player_id, text, channel, flags): 631 | if 1 != self.__state: 632 | return 0 633 | if player_id == -1: 634 | self.sendEventMessage("chatServer", channel, flags, text) 635 | else: 636 | player = bf2.playerManager.getPlayerByIndex(player_id) 637 | self.sendEventMessage("chatPlayer", channel, flags, player.index, stripmessage(text)) 638 | 639 | 640 | class OutputBuffer(object): 641 | """A stateful output buffer. 642 | 643 | This knows how to enqueue data and ship it out without blocking. 644 | """ 645 | 646 | def __init__(self, modManager, socket, allowBatching): 647 | self.mm = modManager 648 | self.allowBatching = allowBatching 649 | self.socket = socket 650 | self.data = [] 651 | self.index = 0 652 | 653 | def enqueue(self, str): 654 | try: 655 | self.data.append(str) 656 | except Exception, e: 657 | self.mm.error("Failed to enqueue '%s' (%s)" % (str, e), True) 658 | 659 | def update(self): 660 | while len(self.data) > 0: 661 | try: 662 | item = self.data[0] 663 | scount = self.socket.send(item[self.index:]) 664 | self.index += scount 665 | if self.index == len(item): 666 | del self.data[0] 667 | self.index = 0 668 | except socket.error, detail: 669 | if detail[0] != errno.EWOULDBLOCK: 670 | self.mm.error("Failed to send", True) 671 | return detail[1] 672 | if not self.allowBatching: 673 | break 674 | return None 675 | 676 | 677 | # Helper methods 678 | # TODO: Save id on the object 679 | def findId(object): 680 | pos = object.getPosition() 681 | ids = getObjectsIdsOfTemplate(object.templateName) 682 | for id in ids: 683 | try: 684 | host.rcon_invoke("object.active id%s" % str(id)) 685 | pos2 = host.rcon_invoke('object.absoluteposition') 686 | pos2 = map(float, pos2.split('/')) 687 | 688 | # Distance within 1m of the object we're checking 689 | if vectorDistance(pos, pos2) < 1: 690 | # host.rcon_invoke('echo "id: %s"' % str(id)) 691 | return id 692 | 693 | except Exception, detail: 694 | host.rcon_invoke('echo "woops: %s"' % detail) 695 | return -1 696 | 697 | 698 | def vectorDistance(u, v): 699 | d = [math.fabs(a - b) for a, b in zip(u, v)] 700 | return math.sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]) 701 | 702 | 703 | def setPlayerPosition(player, pos): 704 | if player and player.isAlive(): 705 | playerVehicle = player.getVehicle() 706 | rootVehicle = bf2.objectManager.getRootParent(playerVehicle) 707 | rootVehicle.setPosition((pos[0], pos[1], pos[2])) 708 | 709 | 710 | def setPlayerRotation(player, rot): 711 | if player and player.isAlive(): 712 | playerVehicle = player.getVehicle() 713 | rootVehicle = bf2.objectManager.getRootParent(playerVehicle) 714 | rootVehicle.setRotation((rot[0], rot[1], rot[2])) 715 | 716 | 717 | def setVehiclePosition(vehicle, pos): 718 | if vehicle and pos: 719 | rootVehicle = bf2.objectManager.getRootParent(vehicle) 720 | rootVehicle.setPosition((pos[0], pos[1], pos[2])) 721 | 722 | 723 | def setPlayerDamage(player, damage): 724 | if player and player.isAlive(): 725 | try: 726 | soldier = player.getDefaultVehicle() 727 | rootVehicle = bf2.objectManager.getRootParent(player.getVehicle()) 728 | rootVehicle.setDamage(damage) 729 | if damage < 10: 730 | soldier.setDamage(damage) 731 | sid = findId(rootVehicle) 732 | host.rcon_invoke('object.active id%s' % str(sid)) 733 | host.rcon_invoke('object.delete') 734 | vid = findId(rootVehicle) 735 | host.rcon_invoke('object.active id%s' % str(vid)) 736 | host.rcon_invoke('object.delete') 737 | except Exception, detail: 738 | pass 739 | 740 | 741 | def awardPlayer(player, medalnum, medalval): 742 | #medals = { 'gold': 2051907, 'silver': 2051919, 'bronze': 2051902, 'ph': 2191608, 'bm': 2190703 } 743 | if player: 744 | bf2.gameLogic.sendMedalEvent(p, medalnum, medalval) 745 | 746 | 747 | def setPlayerRank(player, ranknum, rankevent): 748 | # rankevent True then it pops up on the player's screen 749 | if player: 750 | # Ranks 0-21 only 751 | if ranknum >= 0 and ranknum <= 21: 752 | if rankevent: 753 | bf2.gameLogic.sendRankEvent(player, ranknum, 0) 754 | player.score.rank = ranknum 755 | else: 756 | player.score.rank = ranknum 757 | 758 | 759 | def getPositionString(obj): 760 | pos = bf2.objectManager.getRootParent(obj).getPosition() 761 | return "%.1f/%.1f/%.1f" % (round(pos[0], 1), round(pos[1], 1), round(pos[2], 1)) 762 | 763 | 764 | def getRotationString(obj): 765 | rot = bf2.objectManager.getRootParent(obj).getRotation() 766 | return "%.1f/%.1f/%.1f" % (round(rot[0], 1), round(rot[1], 1), round(rot[2], 1)) 767 | 768 | 769 | def getVectorDistance(pos1, pos2): 770 | diffVec = [0.0, 0.0, 0.0] 771 | diffVec[0] = math.fabs(pos1[0] - pos2[0]) 772 | diffVec[1] = math.fabs(pos1[1] - pos2[1]) 773 | diffVec[2] = math.fabs(pos1[2] - pos2[2]) 774 | 775 | # Application of Pythagorean theorem to calculate total distance 776 | return math.sqrt(diffVec[0] * diffVec[0] + diffVec[1] * diffVec[1] + diffVec[2] * diffVec[2]) 777 | 778 | 779 | def getMapList(): 780 | maplist = host.rcon_invoke("maplist.list") 781 | pattern = re.compile(r'^\d+:\s\"(.*?)\"\sgpm_cq\s(\d{2})$', re.MULTILINE) 782 | result = [] 783 | for maplist in pattern.findall(maplist): 784 | result.append(maplist[0].lower() + "|" + maplist[1]) 785 | return result 786 | 787 | 788 | def stripmessage(text): 789 | text = text.replace("HUD_TEXT_CHAT_TEAM", "") 790 | text = text.replace("HUD_TEXT_CHAT_SQUAD", "") 791 | text = text.replace("HUD_TEXT_CHAT_COMMANDER", "") 792 | text = text.replace("HUD_CHAT_DEADPREFIX", "") 793 | #text = text.replace("�1DEAD�0", "DEAD") 794 | text = text.replace("*�1DEAD�0*", "") 795 | text = text.replace("*DEAD*", "") 796 | text = text.replace("�1DEAD�0", "") 797 | return text 798 | 799 | 800 | def get_int(string): 801 | try: 802 | num = int(string.strip('"\' ')) 803 | except ValueError: 804 | num = None 805 | return num 806 | 807 | 808 | def getObjectsIdsOfTemplate(templateName): 809 | arr = [] 810 | try: 811 | objs = host.rcon_invoke(('object.listObjectsOfTemplate ' + str(templateName))) 812 | objs = objs.split() 813 | for i in range((len(objs) / 10)): 814 | objectId = ((i * 10) + 3) 815 | arr.append(objs[objectId]) 816 | except: 817 | pass 818 | return arr 819 | 820 | 821 | # 822 | # ModManager load 823 | # 824 | def mm_load(modManager): 825 | """Creates and returns your object.""" 826 | return WebAdmin(modManager) 827 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/adminutils/demo/rotate_demo.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # rotate_demo.py: simple file-rotation and index updating script 4 | # 5 | # Requires Python 2.3 or newer. 6 | # 7 | # Theory of operation: 8 | # When automatic demo recording is enabled in the BF2 dedicated server it will 9 | # call a hook program (such as this) when a new demo file is ready for 10 | # publishing. The server will wait for the hook program to complete before 11 | # notifying connected clients of the URL the demo can be downloaded from. It is 12 | # therefore important that all work is done in a blocking manner in this 13 | # program, or clients might try to download demos that aren't in place on the 14 | # web server yet. 15 | # 16 | # Copyright (c)2004 Digital Illusions CE AB 17 | # Author: Andreas Fredriksson 18 | 19 | import os 20 | import sys 21 | import shutil 22 | 23 | # for debugging a hack like this might be useful since stdout and stderr are 24 | # discarded when the script is run 25 | # class writer: 26 | # def __init__(self): 27 | # self.stream = open('log.txt', 'w') 28 | # def write(self, str): 29 | # self.stream.write(str) 30 | # 31 | #sys.stdout = writer() 32 | 33 | # helper function to create directories as needed -- this doesn't care about 34 | # umask or permissions in general so consider this a starting point 35 | 36 | 37 | def ensure_exists(path): 38 | try: 39 | os.stat(path) 40 | except: 41 | try: 42 | os.makedirs(path) 43 | except: 44 | pass 45 | 46 | 47 | # set some sane defaults 48 | options = { 49 | 'use_ftp': '0', 50 | 'ftp_server': '', 51 | 'ftp_target_dir': '', 52 | 'ftp_user': None, 53 | 'ftp_password': None, 54 | 'target_root': 'webroot', 55 | 'file_limit': '10', 56 | } 57 | 58 | # parse the config file, if it's there 59 | try: 60 | config = open('rotate_demo.cfg', 'rt') 61 | for line_ in config: 62 | line = line_.strip() 63 | if len(line) == 0 or line.startswith('#'): 64 | continue 65 | try: 66 | key, value = line.split('=') 67 | options[key.strip()] = value.strip() 68 | except ValueError, ex: 69 | print ex 70 | except IOError: 71 | pass 72 | 73 | # our first argument indicates the demo file which is ready to be moved 74 | path = os.path.normpath(sys.argv[1].replace('"', '')) 75 | 76 | # handle local file shuffling (web server on same host as bf2 server, or on network share) 77 | if options['use_ftp'] == '0': 78 | # this is our target directory (i.e. the download dir) 79 | target_demo_dir = os.path.join(options['target_root'], 'demos/uploaded') 80 | 81 | # create the directory structure if it doesn't exist 82 | ensure_exists(options['target_root']) 83 | ensure_exists(os.path.join(options['target_root'], 'demos/uploaded')) 84 | 85 | # don't move if path and target are the same 86 | if os.path.abspath(os.path.dirname(path)) != os.path.abspath(target_demo_dir): 87 | try: 88 | # NOTE: this requires atleast Python 2.3 89 | print "moving '%s' to '%s'" % (path, target_demo_dir) 90 | shutil.move(path, target_demo_dir) 91 | except IOError: 92 | sys.exit(1) 93 | 94 | timestamped = [] 95 | 96 | # get a list of .bf2demo files in the target dir (including our own file) 97 | for pf in filter(lambda x: x.endswith('.bf2demo'), os.listdir(target_demo_dir)): 98 | try: 99 | ppath = os.path.join(target_demo_dir, pf) 100 | os.chmod(ppath, 0644) # make web-readable 101 | timestamped.append((os.stat(ppath).st_mtime, ppath)) 102 | except IOError: 103 | pass # don't let I/O errors stop us 104 | 105 | # sort the timestamped file list according to modification time 106 | # NOTE: this sort is reversed so that older files are at the end of the list 107 | def compare_times(f1, f2): return cmp(f2[0], f1[0]) # note reverse sort order 108 | timestamped.sort(compare_times) 109 | 110 | # delete the oldest files to meet the file limit 111 | file_limit = int(options['file_limit']) 112 | for timestamp, deletium in timestamped[file_limit:]: 113 | try: 114 | os.remove(deletium) 115 | except IOError: 116 | pass # file in use? 117 | 118 | # create the index file 119 | if 0: # dep: I guess this is superfluous 120 | idxf = open(os.path.join(options['target_root'], 'index.lst'), 'w') 121 | for timestamp, keptfile in timestamped[:file_limit]: 122 | fn = keptfile.split(os.sep)[-1] 123 | idxf.write('demos/%s\n' % (fn)) 124 | idxf.close() 125 | 126 | else: # use ftp 127 | try: 128 | import ftplib 129 | import re 130 | 131 | path.replace('\\\\', '\\') 132 | 133 | path = os.path.normpath(path).replace('\\', '/') 134 | 135 | fn = path 136 | idx = fn.rfind('/') 137 | if idx != -1: 138 | fn = fn[idx+1:] 139 | 140 | demof = open(path, 'rb') 141 | 142 | # set up ftp connection and change cwd 143 | ftp = ftplib.FTP(options['ftp_server'], options['ftp_user'], options['ftp_password']) 144 | ftp.cwd(options['ftp_target_dir']) 145 | 146 | file_limit = int(options['file_limit']) 147 | 148 | try: 149 | files = ftp.nlst() 150 | except Exception: 151 | files = [] 152 | files = filter(lambda x: x.endswith('.bf2demo'), files) 153 | files.sort() 154 | 155 | # store the new file 156 | ftp.storbinary('STOR '+fn, demof) 157 | 158 | demof.close() 159 | 160 | try: 161 | # delete local file 162 | os.unlink(path) 163 | except OSError: 164 | # couldn't unlink local file, what to do? 165 | pass 166 | 167 | # handle rotation 168 | while len(files) + 1 > file_limit: 169 | # dep: nb: this relies on the data formatting in the bf2demo filenames 170 | # if you have other 171 | #print 'deleting %s' % (files[0]) 172 | ftp.delete(files[0]) 173 | del files[0] 174 | 175 | # bye bye 176 | ftp.quit() 177 | 178 | except Exception, detail: 179 | import traceback 180 | log = open('rotate_demo_err.txt', 'w') 181 | ex = sys.exc_info() 182 | traceback.print_exception(ex[0], ex[1], ex[2], 16, log) 183 | log.write('\n') 184 | log.close() 185 | sys.exit(1) 186 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/mods/bf2/settings/modmanager.con: -------------------------------------------------------------------------------- 1 | # 2 | # Multiplay, ModManager 3 | # 4 | modmanager.autoSave 1 5 | modmanager.banManagerModule "mm_banmanager" 6 | modmanager.debugEnable 0 7 | modmanager.debugFile "modmanager_debug.log" 8 | modmanager.homeGuess "C:/Documents and Settings/Administrator/My Documents/Battlefield 2/" 9 | modmanager.logAppend 0 10 | modmanager.logAutoFlush 1 11 | modmanager.logDateFormat "[%Y-%m-%d %H:%M:%S] " 12 | modmanager.logLevel 2 13 | modmanager.logModule "mm_logger" 14 | modmanager.moduleBase "modules" 15 | modmanager.rconModule "mm_rcon" 16 | 17 | # Modules 18 | #modmanager.loadModule "mm_tk_punish" 19 | #modmanager.loadModule "mm_kicker" 20 | #modmanager.loadModule "mm_announcer" 21 | modmanager.loadModule "mm_bf2cc" 22 | modmanager.loadModule "mm_autobalance" 23 | #modmanager.loadModule "mm_reserver" 24 | modmanager.loadModule "mm_iga" 25 | modmanager.loadModule "mm_webadmin" 26 | 27 | # 28 | # ModManager Announcer 29 | # 30 | #mm_announcer.addTimedMessage "30:300:Server Rules: No team killing, no stats padding, keep the teams balanced and play fair!" 31 | 32 | # 33 | # ModManager Team autobalance 34 | # 35 | mm_autobalance.allowCommander 0 36 | mm_autobalance.allowSquadLeader 0 37 | mm_autobalance.allowSquadMember 0 38 | mm_autobalance.roundSwitch 1 39 | 40 | # 41 | # BF2CC for ModManager 42 | # 43 | mm_bf2cc.chatBufferSize 50 44 | mm_bf2cc.serverChatFormat "[Admin: %s] %s" 45 | 46 | # 47 | # ModManager Logger 48 | # 49 | mm_logger.logAppend 0 50 | mm_logger.logAutoFlush 1 51 | mm_logger.logFilename "modmanager.log" 52 | 53 | # 54 | # ModManager Player Kicker 55 | # 56 | mm_kicker.banLimit 1 57 | mm_kicker.banPeriod "Round" 58 | mm_kicker.banWordReason "Using bad / racist language" 59 | mm_kicker.enableChatChecks 0 60 | mm_kicker.idleIgnoreNotStarted 1 61 | mm_kicker.idleLimit 0 62 | mm_kicker.initDelay 60 63 | mm_kicker.kickDelay 5 64 | mm_kicker.kickLimit 3 65 | mm_kicker.kickMessage "Sorry '%s' your are being kicked ( %s )" 66 | mm_kicker.kickType 1 67 | mm_kicker.kickWordReason "Using bad / racist language" 68 | mm_kicker.maxPing 0 69 | mm_kicker.minPing 0 70 | mm_kicker.negScoreKick 0 71 | mm_kicker.pingLimit 0 72 | mm_kicker.positionDelay 120 73 | mm_kicker.samplePeriod 120 74 | mm_kicker.sampleRate 120 75 | mm_kicker.warnWordMessage "WARNING: Please refrain from using bad / racist language on this server '%s'" 76 | mm_kicker.chatSpamLimit 100 77 | mm_kicker.chatSpamPeriod 1 78 | 79 | # 80 | # ModManager Rcon 81 | # 82 | mm_rcon.allowBatching 1 83 | mm_rcon.basicAuthLevel 50 84 | mm_rcon.enableLinger 0 85 | mm_rcon.lingerFor 1 86 | mm_rcon.logCommands 0 87 | mm_rcon.loginMessage "" 88 | mm_rcon.logoutMessage "" 89 | mm_rcon.rconBasicPassword "{{rcon_password}}" 90 | mm_rcon.rconIp "0.0.0.0" 91 | mm_rcon.rconListenQueue 1 92 | mm_rcon.rconPassword "{{rcon_password}}" 93 | mm_rcon.rconPort 4711 94 | mm_rcon.reuseAddress 1 95 | mm_rcon.superAuthLevel 100 96 | mm_rcon.defaultGametype "gpm_cq" 97 | mm_rcon.advancedMapSizeValidation 0 98 | 99 | # 100 | # ModManager Reserver 101 | # 102 | mm_reserver.kickDelay 5 103 | mm_reserver.kickReason "Reserved slots reached" 104 | mm_reserver.kickMode 2 105 | mm_reserver.kickType 1 106 | mm_reserver.privatePassword "" 107 | mm_reserver.reservedSlots 1 108 | 109 | # 110 | # ModManager Team kill punisher 111 | # 112 | mm_tk_punish.announcePunishments 1 113 | mm_tk_punish.banMessageDelay 5 114 | mm_tk_punish.bannedBy "ModManager Team Kill Punisher" 115 | mm_tk_punish.banPeriod "Round" 116 | mm_tk_punish.banReason "Team killing" 117 | mm_tk_punish.forgiveMessage "TKPUNISH: %s forgives %s for a teamkill (%s has %d punishes and %d forgives)" 118 | mm_tk_punish.punishMessage "TKPUNISH: %s punishes %s for a teamkill (%s has %d punishes and %d forgives)" 119 | mm_tk_punish.punishTime 20 120 | 121 | # 122 | # ModManager BanManager 123 | # 124 | mm_banmanager.banFilename "mm_bans.xml" 125 | mm_banmanager.banMessage "%s you are being banned (reason: %s)" 126 | mm_banmanager.defaultBanAddress "N/A" 127 | mm_banmanager.defaultBanCdKeyHash "N/A" 128 | mm_banmanager.defaultBanDelay 5 129 | mm_banmanager.defaultBanMethod "Key" 130 | mm_banmanager.defaultBanNick "N/A" 131 | mm_banmanager.defaultBanPeriod "Perm" 132 | mm_banmanager.defaultBanReason "Unknown" 133 | mm_banmanager.defaultKickDelay 5 134 | mm_banmanager.defaultKickReason "Unknown" 135 | mm_banmanager.defaultUnBanReason "Unknown" 136 | mm_banmanager.kickMessage "%s you are being kicked (reason: %s)" 137 | mm_banmanager.dateTimeFormat "%d/%m/%Y %H:%M:%S %Z" 138 | mm_banmanager.oldDateTimeFormat "%a %b %d %H:%M:%S %Y" 139 | 140 | # 141 | # In Game Admin v1.6 142 | # 143 | mm_iga.addAdmin "d975d59a9b32e9f105a15667a18e93d7:all" 144 | mm_iga.authLevel 100 145 | mm_iga.addCmdBinding "k|kick:iga kick" 146 | mm_iga.addCmdBinding "b|ban:iga ban" 147 | mm_iga.addCmdBinding "m|map:map" 148 | mm_iga.addCmdBinding "s|say:exec game.sayAll" 149 | mm_iga.addCmdBinding "sw|switch:bf2cc switchplayer" 150 | mm_iga.addCmdBinding "w|warn:iga warn" 151 | mm_iga.addCmdBinding "r|restart:admin.restartMap" 152 | mm_iga.addCmdBinding "n|next:admin.runNextLevel" 153 | mm_iga.addCmdBinding "l|list:admin.listPlayers" 154 | mm_iga.addCmdBinding "p|pause:exec gameLogic.togglePause" 155 | mm_iga.cmdPrefix "!" 156 | mm_iga.notAdminMessage "Sorry %s you are not registered as an admin!" 157 | mm_iga.notAuthedMessage "Sorry %s you are not permitted use the command %s" 158 | mm_iga.warningAction "Warning" 159 | mm_iga.warningPrefix "[%admin%] " 160 | mm_iga.addWarning "tk|team killing:%action% %player% stop Team Killing!" 161 | mm_iga.addWarning "lang|language:%action% %player% stop using Bad Language!" 162 | mm_iga.addWarning "vh|vehicle whore:%action% %player% stop Stealing/Shooting Teammates Vehicles!" 163 | mm_iga.addWarning "hp|high ping:%action% %player% High Ping!" 164 | mm_iga.addWarning "spam|spamming:%action% %player% stop Spamming Messages!" 165 | mm_iga.addWarning "i|idle:%action% %player% for being Idle!" 166 | mm_iga.addWarning "sp|Cheating Stats:%action% %player% stop Stat Padding!" 167 | mm_iga.addWarning "mr|make room for admin:%action% %player% to make room for an Admin" 168 | mm_iga.addWarning "nv|name violation:%action% %player% for Name Violation!" 169 | 170 | # 171 | # Web Admin 172 | # 173 | mm_webadmin.serverHost "{{bf2wa_host}}" 174 | mm_webadmin.serverPort {{bf2wa_port}} 175 | mm_webadmin.timerInterval {{bf2wa_timer_interval}} 176 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/mods/bf2/settings/serversettings.con: -------------------------------------------------------------------------------- 1 | sv.serverName "{{server_name}}" 2 | sv.password "" 3 | sv.internet 1 4 | sv.serverIP "0.0.0.0" 5 | sv.serverPort {{server_port}} 6 | sv.welcomeMessage "" 7 | sv.punkBuster 1 8 | sv.allowFreeCam 1 9 | sv.allowExternalViews 1 10 | sv.allowNoseCam 1 11 | sv.hitIndicator 1 12 | sv.maxPlayers {{max_players}} 13 | sv.numPlayersNeededToStart 2 14 | sv.notEnoughPlayersRestartDelay 15 15 | sv.startDelay 15 16 | sv.endDelay 15 17 | sv.spawnTime 15 18 | sv.manDownTime 15 19 | sv.endOfRoundDelay 15 20 | sv.ticketRatio 100 21 | sv.roundsPerMap 3 22 | sv.timeLimit 0 23 | sv.scoreLimit 0 24 | sv.soldierFriendlyFire 100 25 | sv.vehicleFriendlyFire 100 26 | sv.soldierSplashFriendlyFire 100 27 | sv.vehicleSplashFriendlyFire 100 28 | sv.tkPunishEnabled 0 29 | sv.tkNumPunishToKick 0 30 | sv.tkPunishByDefault 0 31 | sv.votingEnabled 1 32 | sv.voteTime 90 33 | sv.minPlayersForVoting 2 34 | sv.teamVoteOnly 0 35 | sv.gameSpyPort {{gamespy_port}} 36 | sv.allowNATNegotiation 0 37 | sv.interfaceIP "0.0.0.0" 38 | sv.autoRecord 0 39 | sv.demoIndexURL {{demos_url}} 40 | sv.demoDownloadURL {{demos_url}} 41 | sv.autoDemoHook "adminutils/demo/rotate_demo.py" 42 | sv.demoQuality 10 43 | sv.adminScript "modmanager" 44 | sv.timeBeforeRestartMap 5 45 | sv.autoBalanceTeam 0 46 | sv.teamRatioPercent 100 47 | sv.voipEnabled 1 48 | sv.voipQuality 5 49 | sv.voipServerRemote 0 50 | sv.voipServerRemoteIP "" 51 | sv.voipServerPort 55126 52 | sv.voipBFClientPort 55123 53 | sv.voipBFServerPort 55124 54 | sv.voipSharedPassword "" 55 | sv.useGlobalRank 0 56 | sv.useGlobalUnlocks 1 57 | sv.sponsorText "" 58 | sv.sponsorLogoURL "" 59 | sv.communityLogoURL "" 60 | sv.radioSpamInterval 6 61 | sv.radioMaxSpamFlagCount 6 62 | sv.radioBlockedDurationTime 30 63 | sv.numReservedSlots 0 64 | sv.friendlyFireWithMines 0 65 | sv.coopBotCount 32 66 | sv.coopBotDifficulty 50 67 | sv.coopBotRatio 50 68 | sv.noVehicles 0 69 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/dll/ka001393.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/dll/ka001393.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/dll/kc002306.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/dll/kc002306.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/dll/ks001800.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/dll/ks001800.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/htm/ma001393.htm: -------------------------------------------------------------------------------- 1 | Not Yet Supported -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/htm/mc002306.htm: -------------------------------------------------------------------------------- 1 | Not Yet Supported -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbag.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbag.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbags.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbags.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbcl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbcl.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbcls.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbcls.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbns.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbns.dat -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbsec.htm: -------------------------------------------------------------------------------- 1 |

2 |

To Manually update your local PunkBuster Security Files, 3 | choose "Save As" from your Web Browser's "File" Menu and explore to 4 | find your game folder and the subfolder called "pb", then save this file 5 | in that location (/pb/pbsec.htm or /pb/pbsecsv.htm). 6 | The next time you start your game, your PunkBuster will have the latest 7 | security information necessary to accept auto-updates from PB Servers. 8 |

9 |

S 3 MASTER5.EVENBALANCE.COM 16567 0 10 |

S 4 MASTER6.EVENBALANCE.COM 16567 0 11 |

S 5 MASTER7.EVENBALANCE.COM 16567 0 12 |

S 2 BF21.EVENBALANCE.COM 16567 0 13 |

M BF22.EVENBALANCE.COM 16567 14 |

F A K 1393 7B60 48AB9BE59074C258CE2C758DD38F5F2F E14C9AF0134D6C493FEFACD9F684C0B5 15 |

F C K 2306 76CF0 EB739E71D5C5758D9749199CA2DA3BDF 22C8077205319247CD9DE1FE07AAC833 16 |

F S K 1800 BF0F0 8F9302F5BD776347AA15D93B658CFA9D D366F0E8009DF62E4433226F73C7F739 17 |

F A L 1393 112BC F3D5B051895295547B25B730A5532DAB 8F5F431A43F8D3C45D6AF9694D9BDBFC 18 |

F C L 2306 DF764 3FA4720F15D5323180E7EC6740D5DD00 640CFF763A0B491BD430082FEBEBC098 19 |

F S L 1800 144244 60CE7BF03014B2109BF18094B7807937 77040B56295447BC5CBF300034F460ED 20 |

F A M 1393 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 21 |

F C M 2306 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 22 |

F S M 1800 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 23 |

F A W 1393 10000 2EB9DF6C1CA87B2DAD817BF3B66939D4 839BDB6CDCB638290C1C645F30A5FFB6 24 |

F C W 2306 E9F54 019D54A9CD7385B9F32EAE49F87B3493 ED8C3FE257A1EFAC3705394C9A441195 25 |

F S W 1800 66000 B5DA0C51C5806C17696B1C5E11B11855 856849563D318794E1C67FA1B4A027F3 26 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbsecsv.htm: -------------------------------------------------------------------------------- 1 |

2 |

To Manually update your local PunkBuster Security Files, 3 | choose "Save As" from your Web Browser's "File" Menu and explore to 4 | find your game folder and the subfolder called "pb", then save this file 5 | in that location (/pb/pbsec.htm or /pb/pbsecsv.htm). 6 | The next time you start your game, your PunkBuster will have the latest 7 | security information necessary to accept auto-updates from PB Servers. 8 |

9 |

S 3 MASTER5.EVENBALANCE.COM 16567 0 10 |

S 4 MASTER6.EVENBALANCE.COM 16567 0 11 |

S 5 MASTER7.EVENBALANCE.COM 16567 0 12 |

S 2 BF21.EVENBALANCE.COM 16567 0 13 |

M BF22.EVENBALANCE.COM 16567 14 |

F A K 1393 7B60 48AB9BE59074C258CE2C758DD38F5F2F E14C9AF0134D6C493FEFACD9F684C0B5 15 |

F C K 2306 76CF0 EB739E71D5C5758D9749199CA2DA3BDF 22C8077205319247CD9DE1FE07AAC833 16 |

F S K 1800 BF0F0 8F9302F5BD776347AA15D93B658CFA9D D366F0E8009DF62E4433226F73C7F739 17 |

F A L 1393 112BC F3D5B051895295547B25B730A5532DAB 8F5F431A43F8D3C45D6AF9694D9BDBFC 18 |

F C L 2306 DF764 3FA4720F15D5323180E7EC6740D5DD00 640CFF763A0B491BD430082FEBEBC098 19 |

F S L 1800 144244 60CE7BF03014B2109BF18094B7807937 77040B56295447BC5CBF300034F460ED 20 |

F A M 1393 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 21 |

F C M 2306 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 22 |

F S M 1800 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 23 |

F A W 1393 10000 2EB9DF6C1CA87B2DAD817BF3B66939D4 839BDB6CDCB638290C1C645F30A5FFB6 24 |

F C W 2306 E9F54 019D54A9CD7385B9F32EAE49F87B3493 ED8C3FE257A1EFAC3705394C9A441195 25 |

F S W 1800 66000 B5DA0C51C5806C17696B1C5E11B11855 856849563D318794E1C67FA1B4A027F3 26 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbsv.cfg: -------------------------------------------------------------------------------- 1 | ; ------------------------------ 2 | ; General Settings 3 | ; ------------------------------ 4 | pb_sv_MsgPrefix "PunkBuster Server" 5 | pb_sv_MaxDlRate 4 //[KB/sec requested per file (default=4)] 6 | pb_sv_MaxConDls 6 //[Concurrent downloads (default=1)] 7 | pb_sv_MaxSendRate 8 // [speed at which PB Server sends pbcl and pbag updates in KBps (default=8)] 8 | pb_sv_CQC 1 //[0=No, 1=Yes (default=1)] 9 | pb_sv_Sleep 60 //[# of Milliseconds (default=60)] 10 | pb_sv_PowerMin 10 //[Power Points] 11 | pb_sv_PowerDef 1 //[Power Points] 12 | pb_sv_PowerKickLen 5 //[Minutes (default=5)] 13 | pb_sv_DupNameGrace 0 //[Seconds] 14 | pb_sv_ExtChar 0 //[0=No, 1=Yes (default=0)] 15 | pb_sv_GuidRelax 7 //[1=UNKN, 2=WRONGIP, 4=DUP (add desired values)] 16 | pb_sv_RconReload 0 //[0=No, 1=Yes (default=0)] 17 | pb_sv_LogSync 0 //[0=No, 1=Yes (default=0)] 18 | pb_sv_AutoUpdBan 1 //[0=No, 1=Yes (default=0)] 19 | 20 | 21 | ; ------------------------------ 22 | ; Other Settings 23 | ; ------------------------------ 24 | pb_sv_Restrictions 1 //[Kick for Restrictions 0=No, 1=Yes (default=1) 2=key macro Restrictions] 25 | pb_sv_FileWhitelist "" //[Folder Filename Filename ... Filename] 26 | pb_sv_EmptyName 0 //[0=No, 1=Yes (default=0)] 27 | pb_sv_LogFloor 1 //[Low log filename serial #] 28 | pb_sv_MinName 0 //[Min Characters in Player name (default=0)] 29 | pb_sv_MaxName 0 //[Max Characters in Player name (default=0)] 30 | pb_sv_LanMask "" //[IP Address Mask for LAN Players (default=)] 31 | pb_sv_Lan 0 //[0=No, 1=Yes (default=0)] 32 | pb_sv_ChangePeriod 600 // For pb_sv_ChangeMax 33 | pb_sv_ChangeMax 5 // [Max name changes allowed in pb_sv_ChangePeriod] ex 5 changes in 600 seconds 34 | 35 | 36 | ; ------------------------------ 37 | ; Kick Settings 38 | ; ------------------------------ 39 | pb_sv_KickLen 1 //[Minutes (default=2)] 40 | pb_sv_ScoreKick 0 //[Min score (negative) ] 41 | pb_sv_UpdateGrace 2400 //[Seconds to wait before Update Failure kick] 42 | pb_sv_NoGuidGrace 10 //[Seconds to wait before No GUID kick] 43 | 44 | 45 | ; ------------------------------ 46 | ; PBSS Settings 47 | ; ------------------------------ 48 | pb_sv_AutoSs 0 //[0=No, 1=Yes (default=0)] 49 | pb_sv_SsFloor 1 //[Low screenshot filename serial #] 50 | pb_sv_SsCeiling 500 //[High screenshot filename serial #] 51 | pb_sv_AutoSsFrom 200 //[Min # of seconds to wait before requesting next ss] 52 | pb_sv_AutoSsTo 1200 //[Max # of seconds to wait before requesting next ss] 53 | pb_sv_SsCmd "" //[Filename of system command to run after screenshots] 54 | pb_sv_SsWidth 1024 //[Requested pixel width of remote screenshots] 55 | pb_sv_SsHeight 768 //[Requested pixel height of remote screenshots] 56 | pb_sv_SsXpct 50 //[Percentage across screen for remote screenshots] 57 | pb_sv_SsYpct 50 //[Percentage down screen for remote screenshots] 58 | pb_sv_SsSrate 2 //[Sample Rate for remote screenshots] 59 | pb_sv_SsDelay 3 //[Maximum delay client waits before capturing screenshot] 60 | pb_sv_SsPath "" //[Path where remote screenshots are saved] 61 | pb_sv_ssLogging 3 //[1=Reg. Log, 2=SS Log, 3=Both, 0=Neither] 62 | pb_sv_ssTimeout 300 //[Seconds] 63 | 64 | 65 | ; ------------------------------ 66 | ; Web Tool Settings 67 | ; ------------------------------ 68 | pb_sv_HttpPort 0 //[Port #] 69 | pb_sv_HttpAddr "" //[External IP Address] 70 | pb_sv_HttpKey "" //[Password] 71 | pb_sv_HttpRefresh 30 //[Seconds] 72 | pb_sv_HttpMaps "" //[Map list (separate by spaces)] 73 | pb_sv_HttpMapsPath "" //[Path where maps are loaded from in WebTool] 74 | pb_sv_HttpColText1 "FFFFFF" //[Text Color #1 in WebTool (default=FFFFFF)] 75 | pb_sv_HttpColText2 "0000FF" //[Text Color #2 in WebTool (default=0000FF)] 76 | pb_sv_HttpColBack1 "000000" //[Background Color #1 in WebTool (default=000000)] 77 | pb_sv_HttpColBack2 "808080" //[Background Color #2 in WebTool (default=808080)] 78 | pb_sv_HttpColLine1 "FF0000" //[Line Color #1 in WebTool (default=FF0000)] 79 | pb_sv_HttpColLine2 "0000FF" //[Line Color #2 in WebTool (default=0000FF)] 80 | pb_sv_HttpColMsg "FF0000" //[Message Color in WebTool (default=FF0000)] 81 | pb_sv_HttpShowGuid 1 //[0=No, 1=Yes (default=0)] 82 | 83 | 84 | ; ------------------------------ 85 | ; MD5 Tool Settings 86 | ; ------------------------------ 87 | pb_sv_md5toolfreq 100 // [Reducing this will increase the scan frequency that can cause lag and also increases the chances being kicked for Ignoring MD5 Tool Queries.] 88 | 89 | 90 | ; ------------------------------ 91 | ; Alias Settings (Only for supported games) 92 | ; ------------------------------ 93 | pb_sv_AliasFn "pbalias.dat" //[Filename (default="pbalias.dat")] 94 | pb_sv_AliasAutoLoad 0 //[0=No, 1=Yes (default=0)] 95 | pb_sv_AliasMax 10 //[Max # of Aliases to track for each PB GUID] 96 | pb_sv_AliasMaxEnforce 0 //[0=No, 1=Yes (default=0)] 97 | 98 | 99 | ; ------------------------------ 100 | ; PB Tasks (pb_sv_task [start delay (seconds)] [repeat (seconds)] [command]) 101 | ; ------------------------------ 102 | pb_sv_task 0 7200 pb_sv_ver // Keep-Alive for PBBans Hub (Do not remove. Will cause servers to appear as inactive when empty for long periods of time) 103 | pb_sv_task 0 86400 pb_sv_update // Check for PB updates daily (More dependable than UCON system) 104 | pb_sv_task 300 691200 pb_sv_md5toolempty // re-update MD5Tool list 105 | pb_sv_task 340 691200 pb_sv_load pbsvuser_mods_md5.cfg 106 | 107 | 108 | ; ------------------------------ 109 | ; Badname List (pb_sv_badname [grace_period_secs] [disallowed text]) 110 | ; ------------------------------ 111 | pb_sv_badnameempty 112 | 113 | 114 | ; ------------------------------ 115 | ; PB UCON Settings / Lists 116 | ; ------------------------------ 117 | pb_sv_usessionlimit 10 118 | pb_sv_ucontimeout 300 119 | pb_sv_uconempty 120 | pb_sv_uconadd 1 66.55.152.232 "pbbhub3-1" "pbbanshub" 121 | pb_sv_uconadd 1 66.55.152.233 "pbbhub3-2" "pbbanshub" 122 | pb_sv_uconadd 1 66.55.152.234 "pbbhub3-3" "pbbanshub" 123 | pb_sv_ProtectTag 1 GGC 124 | pb_sv_uconadd 1 31.214.160.253 "ggc" "bf2" 125 | 126 | 127 | ; ------------------------------ 128 | ; PB UCON Ignore List (Prevents UCON users from sending various commands) 129 | ; ------------------------------ 130 | pb_sv_uconignoreempty 131 | pb_sv_uconignore pb_sv_uconignoreempty 132 | pb_sv_uconignore quit 133 | pb_sv_uconignore pb_sv_md5toolfreq 134 | pb_sv_uconignore pb_sv_GuidRelax 135 | 136 | 137 | ; ------------------------------ 138 | ; Misc 139 | ; ------------------------------ 140 | pb_sv_writecfg pbucon.use // Enables UCON in case the server removes the file for any reason. 141 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbsv.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbsv.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/pb_amd-64/pbsvuser_mods_md5.cfg: -------------------------------------------------------------------------------- 1 | pb_sv_md5tool a "" v mods\bf2\Booster_client.zip SZ169667159 AT0 LEN2048 99D4DA6EA34CA44A6F77B746F61AF9BC 2 | pb_sv_md5tool a "" v mods\bf2\Common_client.zip SZ149828360 AT0 LEN2048 D7A9B566A56DE4DAC0629E9A2BD01E93 3 | pb_sv_md5tool a "" v mods\bf2\Fonts_client.zip SZ12284200 AT0 LEN2048 82DF5D46C2965616E23899185CBDDB12 4 | pb_sv_md5tool a "" v mods\bf2\Menu_client.zip SZ42196891 AT0 LEN2048 158F9C949BF01591C5079239FD871725 5 | pb_sv_md5tool a "" v mods\bf2\Objects_client.zip SZ641042098 AT0 LEN2048 691C0339D9BFDD885627F2A9EFECC058 6 | pb_sv_md5tool a "" v mods\bf2\Shaders_client.zip SZ224632 AT0 LEN2048 6D61C1FC1531A93B2E10C1632B23C350 7 | pb_sv_md5tool a "" v mods\bf2\ClientArchives.con SZ306 AT0 LEN256 3C80942192F21D95FE6FBA3907955DAB 8 | pb_sv_md5tool a "" v mods\bf2\GameLogicInit.con SZ13011 AT0 LEN2048 D9D74730755B6C83EBB2993B79692525 9 | pb_sv_md5tool a "" v mods\bf2\Init.con SZ301 AT0 LEN256 3872BE35FB5754D9ABB4A8D3FBF82066 10 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/bf2/rotate_demo.cfg: -------------------------------------------------------------------------------- 1 | # set the number of demo files to keep in rotation 2 | file_limit = 50 3 | 4 | # for local web server; edit this path and make it match the one in serversettings.con 5 | #target_root = /var/www/html 6 | target_root = /volume 7 | 8 | # set to 1 to enable ftp uploading 9 | use_ftp = 0 10 | 11 | # set to the target directory on the ftp server 12 | ftp_target_dir = /path/to/webroot/demos 13 | 14 | # login information for the ftp server goes here 15 | ftp_server = my.ftp.server 16 | ftp_user = my_user 17 | ftp_password = my_password 18 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | TMP='/home/bf2/tmp' 6 | 7 | INSTALLER="$TMP/bf2-linuxded-1.5.3153.0-installer.sh" 8 | INSTALLER_TGZ="$TMP/bf2-linuxded-1.5.3153.0-installer.tgz" 9 | BF2HUB_TGZ="$TMP/BF2Hub-Unranked-Linux-R3.tar.gz" 10 | MODMANAGER_ZIP="$TMP/ModManager-v2.2c.zip" 11 | 12 | # Get required packages 13 | apt -y update 14 | apt-get -y update 15 | apt-get -y install wget expect unzip 16 | 17 | # Download missing assets 18 | while IFS=" " read url filename 19 | do 20 | args=(-nc -q --show-progress --progress=bar:force:noscroll) 21 | if [ -n "$filename" ]; then 22 | args+=(-O "$filename") 23 | fi 24 | wget "${args[@]}" "$url" 25 | done < assets.txt 26 | 27 | # Verify checksums 28 | if ! sha512sum -w -c assets.sha512; then 29 | echo 'Downloaded file checksum mismatch. Exiting.'; 30 | exit 1; 31 | fi 32 | 33 | # Extract server files from the installer 34 | tar -xvf $INSTALLER_TGZ -C $TMP 35 | chmod +x $INSTALLER ./extract 36 | ./extract 37 | 38 | # Move BF2Hub files into server directory 39 | tar -xvf $BF2HUB_TGZ -C "$TMP/srv" 40 | 41 | # Move ModManager files into server directory 42 | unzip $MODMANAGER_ZIP -d "$TMP/srv" 43 | 44 | # Clean up unused folders (we have updated pb) 45 | rm -r $TMP/srv/pb_* $TMP/srv/bin/ia-32 46 | 47 | # Replace with our own BF2 server files (custom settings and scripts) 48 | cp -r "$TMP/bf2/." "$TMP/srv" 49 | 50 | # Create empty server folder to copy our files into if it's empty on the host system 51 | mkdir -p $VOLUME 52 | chmod -R 700 $VOLUME/ 53 | 54 | exit 0 55 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/build/extract: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect 2 | 3 | set timeout -1 4 | spawn "./bf2-linuxded-1.5.3153.0-installer.sh" --keep --target ./srv/ 5 | 6 | expect { 7 | eof { send_user "\nunexpected eof in extraction\n"; exit 1 } 8 | "*ress return" 9 | } 10 | 11 | send "^c" 12 | 13 | send_user "\nExtraction finished\n" -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/runtime/nginx/default: -------------------------------------------------------------------------------- 1 | # Default nginx server configuration 2 | # 3 | server { 4 | listen 80 default_server; 5 | listen [::]:80 default_server; 6 | 7 | # root /var/www/html; 8 | root /volume/www; 9 | 10 | # Add index.php to the list if you are using PHP 11 | index index.html index.htm index.nginx-debian.html index.php; 12 | 13 | server_name _; 14 | 15 | location /demos/ { 16 | # First attempt to serve request as file, then 17 | # as directory, then fall back to displaying a 404. 18 | alias /volume/demos/uploaded/; 19 | try_files $uri $uri/ =404; 20 | autoindex on; 21 | autoindex_format json; 22 | } 23 | 24 | location ~ ([^\/]+\.bf2demo)$ { 25 | alias /volume/demos/uploaded/$1; 26 | } 27 | 28 | location ~ \.php$ { 29 | alias /volume/www/; 30 | include snippets/fastcgi-php.conf; 31 | fastcgi_pass unix:/var/run/php/php7.4-fpm.sock; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/runtime/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | TMP='/home/bf2/tmp' 5 | SRV='/home/bf2/srv' 6 | VOLUME='/volume' 7 | 8 | generate_pw() { 9 | echo "$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c10)" 10 | } 11 | 12 | replace_var() { 13 | echo "$3: $1 => $2" 14 | replaceEscaped=$(echo "$2" | sed 's/[&/\]/\\&/g') 15 | sed -i -e "s/$1/$replaceEscaped/g" $3 16 | } 17 | 18 | # Check if target volume is empty 19 | if [ "$(ls -A $SRV)" ]; then 20 | echo "$SRV is not empty. Skipping..." 21 | else 22 | # Move server files to persisted folder (-n without overwriting) 23 | echo "$SRV is empty. Moving server files..." 24 | mv -n $TMP/srv/* $SRV/ 25 | 26 | # Create volume directory for all persisted changes 27 | echo 'Moving persisted data and creating symlinks...' 28 | mkdir -m 777 -p $VOLUME 29 | mkdir -m 777 -p $VOLUME/svlogs 30 | mkdir -m 777 -p $VOLUME/svss 31 | mkdir -m 777 -p $VOLUME/demos 32 | mkdir -m 777 -p $VOLUME/demos/pending 33 | mkdir -m 777 -p $VOLUME/demos/uploaded 34 | mkdir -m 777 -p $VOLUME/www 35 | install -m 777 /dev/null $VOLUME/bf2.log 36 | install -m 777 /dev/null $VOLUME/modmanager.log 37 | install -m 777 /dev/null $VOLUME/pbalias.dat 38 | install -m 777 /dev/null $VOLUME/sv_viol.log 39 | mv -n $SRV/mods/bf2/settings $VOLUME 40 | chmod -R 777 $VOLUME/settings 41 | mv -n /var/www/html/bf2tool.php $VOLUME/www 42 | rm -rf $SRV/mods/bf2/demos 43 | rm -rf $SRV/pb_amd-64/svss 44 | rm -rf $SRV/pb_amd-64/svlogs 45 | ln -sf $VOLUME/settings $SRV/mods/bf2/settings 46 | ln -sf $VOLUME/demos/pending $SRV/mods/bf2/demos 47 | ln -sf $VOLUME/svss $SRV/pb_amd-64/svss 48 | ln -sf $VOLUME/svlogs $SRV/pb_amd-64/svlogs 49 | ln -sf $VOLUME/bf2.log $SRV/bf2.log 50 | ln -sf $VOLUME/modmanager.log $SRV/modmanager.log 51 | ln -sf $VOLUME/pbalias.dat $SRV/pbalias.dat 52 | ln -sf $VOLUME/sv_viol.log $SRV/pb_amd-64/sv_viol.log 53 | 54 | # Set execute permissions 55 | echo 'Setting execute permissions...' 56 | cd $SRV 57 | chmod +x ./start_bf2hub.sh ./bin/amd-64/bf2 58 | chmod -R 777 ./pb_amd-64 59 | chmod -R 777 . # temp D: 60 | 61 | # Set server settings from environment variables 62 | replace_var '{{server_name}}' "${ENV_SERVER_NAME:-"bf2-docker"}" "$SRV/mods/bf2/settings/serversettings.con" 63 | replace_var '{{max_players}}' "${ENV_MAX_PLAYERS:-"16"}" "$SRV/mods/bf2/settings/serversettings.con" 64 | replace_var '{{server_port}}' "${ENV_SERVER_PORT:-"16567"}" "$SRV/mods/bf2/settings/serversettings.con" 65 | replace_var '{{gamespy_port}}' "${ENV_GAMESPY_PORT:-"29900"}" "$SRV/mods/bf2/settings/serversettings.con" 66 | replace_var '{{demos_url}}' "${ENV_DEMOS_URL:-"http://example.com/demos/"}" "$SRV/mods/bf2/settings/serversettings.con" 67 | replace_var '{{rcon_password}}' "${ENV_RCON_PASSWORD:-"$(generate_pw)"}" "$SRV/mods/bf2/settings/modmanager.con" 68 | replace_var '{{bf2wa_host}}' "${ENV_BF2WEBADMIN_HOST:-"host.docker.internal"}" "$SRV/mods/bf2/settings/modmanager.con" 69 | replace_var '{{bf2wa_port}}' "${ENV_BF2WEBADMIN_PORT:-"4300"}" "$SRV/mods/bf2/settings/modmanager.con" 70 | replace_var '{{bf2wa_timer_interval}}' "${ENV_BF2WEBADMIN_TIMER_INTERVAL:-"0.25"}" "$SRV/mods/bf2/settings/modmanager.con" 71 | replace_var '{{api_key}}' "${ENV_API_KEY:-"$(generate_pw)"}" "$VOLUME/www/bf2tool.php" 72 | fi 73 | 74 | # Start nginx and php 75 | service nginx start 76 | service php7.4-fpm start 77 | 78 | # Start Battlefield 2 server as the bf2 user 79 | echo "Starting Battlefield 2 server..." 80 | export TERM=xterm 81 | su -c "cd $SRV && ./start_bf2hub.sh >bf2.log" - bf2 82 | 83 | exit 0 84 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/runtime/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | WWW='/var/www/html' 6 | DEMOS="$WWW/demos" 7 | TMP='/home/bf2/tmp' 8 | 9 | # Get required packages and create our user 10 | # libncurses5 = run bf2 11 | # python = rotate_demo.py 12 | # nginx + php7.4-fpm = host demos and bf2tool.php logs 13 | apt -y update 14 | apt-get -y update 15 | apt-get -y install libncurses5 python nginx php7.4-fpm 16 | apt-get clean 17 | rm -rf /var/lib/apt/lists/* 18 | useradd --create-home --shell /bin/bash bf2 19 | 20 | # Replace nginx settings 21 | mv "$TMP/nginx/default" '/etc/nginx/sites-available/' 22 | 23 | # Add PHP tool for log files 24 | mv "$TMP/www/bf2tool.php" $WWW 25 | 26 | # Delete temp files, but not the temp server directory to move during start 27 | find $TMP/* -maxdepth 0 -type d,f -not -name 'srv' -not -name 'run.sh' -exec rm -r "{}" \; 28 | 29 | # Create empty server folder to copy our files into if it's empty on the host system 30 | mkdir -p $VOLUME 31 | chmod -R 700 $VOLUME/ 32 | 33 | # Create demos web folder 34 | mkdir -p $DEMOS 35 | chmod -R 777 $DEMOS/ 36 | 37 | # Change owner 38 | chown -R bf2:bf2 /home/bf2/ 39 | chmod -R 700 /home/bf2/ 40 | 41 | exit 0 42 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/runtime/www/bf2tool.php: -------------------------------------------------------------------------------- 1 | = $getdate) { 19 | $contents = file_get_contents($file); 20 | echo $contents . "\r\n"; 21 | } 22 | } 23 | 24 | // Chat Logs 25 | } else if (isset($_GET['chat']) && isset($_GET['from'])) { 26 | $getdate = intval($_GET['from']); 27 | foreach(glob($bf2folder . 'bf2dchat_*.log') as $file) { 28 | if (filemtime($file) >= $getdate) { 29 | $contents = file_get_contents($file); 30 | echo $contents . "\r\n"; 31 | } 32 | } 33 | 34 | // Get settings 35 | } else if (isset($_GET['getsettings']) && in_array($_GET['getsettings'], $settingsFiles)) { 36 | echo file_get_contents($bf2folder . 'settings/' . $_GET['getsettings']); 37 | 38 | // Set settings 39 | } else if (isset($_GET['setsettings']) && in_array($_GET['setsettings'], $settingsFiles) && $_SERVER['REQUEST_METHOD'] == 'POST') { 40 | $content = file_get_contents("php://input"); 41 | file_put_contents($bf2folder . 'settings/' . $_GET['setsettings'], $content); 42 | 43 | } else { 44 | http_response_code(400); 45 | die('Bad Request'); 46 | } 47 | 48 | } catch (Exception $e) { 49 | echo 'Caught exception: ', $e->getMessage(), "\n"; 50 | } 51 | 52 | ?> -------------------------------------------------------------------------------- /images/bf2hub-pb-mm-webadmin/assets/runtime/www/index.html: -------------------------------------------------------------------------------- 1 | :) -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .vscode 3 | build.bat 4 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM debian:bullseye-slim AS build 3 | 4 | # Add assets to image 5 | WORKDIR /home/bf2/tmp 6 | COPY ./assets/build ./ 7 | 8 | # Download and extract server files 9 | RUN bash -x ./build.sh 10 | 11 | # Runtime stage 12 | FROM debian:bullseye-slim AS runtime 13 | WORKDIR /home/bf2/tmp 14 | LABEL maintainer=nihlen 15 | 16 | # Environment variables 17 | ENV SERVER_NAME="bf2-docker" 18 | 19 | # Copy runtime assets 20 | COPY ./assets/runtime ./ 21 | 22 | # Install required packages and set permissions 23 | RUN bash -x ./setup.sh 24 | 25 | # Copy server files from the build stage 26 | COPY --from=build /home/bf2/tmp/srv ./srv 27 | 28 | # Move server files to persisted folder and start server 29 | CMD ./run.sh 30 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/assets.sha512: -------------------------------------------------------------------------------- 1 | b807684116a0f3d2590390567a5a0da8fa9b0804fb9229ae056fa4cad2d8a46cd306d39592a04167c8b757083e4d20a1d28fe04154c25dab3156ef8be9db3702 bf2-linuxded-1.5.3153.0-installer.tgz 2 | 8391cac06f6667ad4cb495e5ed907159b67ac7c9cb5a1d5fa337d363d95d378767382326add388d06c511653a6295241f64eceffc4648b524cede89e0479a84d BF2Hub-Unranked-Linux-R3.tar.gz 3 | d025aff1a6713da0381b8844f64cd016a66ab907bc936e74ca31ef0781424c02597116c322044da46877766318f28c4c4822e5c4bdec1c19a90157c39fe5bbb4 ModManager-v2.2c.zip 4 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/assets.txt: -------------------------------------------------------------------------------- 1 | https://www.bf-games.net/downloads/mirror/2956 bf2-linuxded-1.5.3153.0-installer.tgz 2 | https://www.bf2hub.com/downloads/BF2Hub-Unranked-Linux-R3.tar.gz 3 | https://static.nihlen.net/bf2/server/ModManager-v2.2c.zip 4 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/adminutils/demo/rotate_demo.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | # 3 | # rotate_demo.py: simple file-rotation and index updating script 4 | # 5 | # Requires Python 2.3 or newer. 6 | # 7 | # Theory of operation: 8 | # When automatic demo recording is enabled in the BF2 dedicated server it will 9 | # call a hook program (such as this) when a new demo file is ready for 10 | # publishing. The server will wait for the hook program to complete before 11 | # notifying connected clients of the URL the demo can be downloaded from. It is 12 | # therefore important that all work is done in a blocking manner in this 13 | # program, or clients might try to download demos that aren't in place on the 14 | # web server yet. 15 | # 16 | # Copyright (c)2004 Digital Illusions CE AB 17 | # Author: Andreas Fredriksson 18 | 19 | import os 20 | import sys 21 | import shutil 22 | 23 | # for debugging a hack like this might be useful since stdout and stderr are 24 | # discarded when the script is run 25 | # class writer: 26 | # def __init__(self): 27 | # self.stream = open('log.txt', 'w') 28 | # def write(self, str): 29 | # self.stream.write(str) 30 | # 31 | #sys.stdout = writer() 32 | 33 | # helper function to create directories as needed -- this doesn't care about 34 | # umask or permissions in general so consider this a starting point 35 | 36 | 37 | def ensure_exists(path): 38 | try: 39 | os.stat(path) 40 | except: 41 | try: 42 | os.makedirs(path) 43 | except: 44 | pass 45 | 46 | 47 | # set some sane defaults 48 | options = { 49 | 'use_ftp': '0', 50 | 'ftp_server': '', 51 | 'ftp_target_dir': '', 52 | 'ftp_user': None, 53 | 'ftp_password': None, 54 | 'target_root': 'webroot', 55 | 'file_limit': '10', 56 | } 57 | 58 | # parse the config file, if it's there 59 | try: 60 | config = open('rotate_demo.cfg', 'rt') 61 | for line_ in config: 62 | line = line_.strip() 63 | if len(line) == 0 or line.startswith('#'): 64 | continue 65 | try: 66 | key, value = line.split('=') 67 | options[key.strip()] = value.strip() 68 | except ValueError, ex: 69 | print ex 70 | except IOError: 71 | pass 72 | 73 | # our first argument indicates the demo file which is ready to be moved 74 | path = os.path.normpath(sys.argv[1].replace('"', '')) 75 | 76 | # handle local file shuffling (web server on same host as bf2 server, or on network share) 77 | if options['use_ftp'] == '0': 78 | # this is our target directory (i.e. the download dir) 79 | target_demo_dir = os.path.join(options['target_root'], 'demos/uploaded') 80 | 81 | # create the directory structure if it doesn't exist 82 | ensure_exists(options['target_root']) 83 | ensure_exists(os.path.join(options['target_root'], 'demos/uploaded')) 84 | 85 | # don't move if path and target are the same 86 | if os.path.abspath(os.path.dirname(path)) != os.path.abspath(target_demo_dir): 87 | try: 88 | # NOTE: this requires atleast Python 2.3 89 | print "moving '%s' to '%s'" % (path, target_demo_dir) 90 | shutil.move(path, target_demo_dir) 91 | except IOError: 92 | sys.exit(1) 93 | 94 | timestamped = [] 95 | 96 | # get a list of .bf2demo files in the target dir (including our own file) 97 | for pf in filter(lambda x: x.endswith('.bf2demo'), os.listdir(target_demo_dir)): 98 | try: 99 | ppath = os.path.join(target_demo_dir, pf) 100 | os.chmod(ppath, 0644) # make web-readable 101 | timestamped.append((os.stat(ppath).st_mtime, ppath)) 102 | except IOError: 103 | pass # don't let I/O errors stop us 104 | 105 | # sort the timestamped file list according to modification time 106 | # NOTE: this sort is reversed so that older files are at the end of the list 107 | def compare_times(f1, f2): return cmp(f2[0], f1[0]) # note reverse sort order 108 | timestamped.sort(compare_times) 109 | 110 | # delete the oldest files to meet the file limit 111 | file_limit = int(options['file_limit']) 112 | for timestamp, deletium in timestamped[file_limit:]: 113 | try: 114 | os.remove(deletium) 115 | except IOError: 116 | pass # file in use? 117 | 118 | # create the index file 119 | if 0: # dep: I guess this is superfluous 120 | idxf = open(os.path.join(options['target_root'], 'index.lst'), 'w') 121 | for timestamp, keptfile in timestamped[:file_limit]: 122 | fn = keptfile.split(os.sep)[-1] 123 | idxf.write('demos/%s\n' % (fn)) 124 | idxf.close() 125 | 126 | else: # use ftp 127 | try: 128 | import ftplib 129 | import re 130 | 131 | path.replace('\\\\', '\\') 132 | 133 | path = os.path.normpath(path).replace('\\', '/') 134 | 135 | fn = path 136 | idx = fn.rfind('/') 137 | if idx != -1: 138 | fn = fn[idx+1:] 139 | 140 | demof = open(path, 'rb') 141 | 142 | # set up ftp connection and change cwd 143 | ftp = ftplib.FTP(options['ftp_server'], options['ftp_user'], options['ftp_password']) 144 | ftp.cwd(options['ftp_target_dir']) 145 | 146 | file_limit = int(options['file_limit']) 147 | 148 | try: 149 | files = ftp.nlst() 150 | except Exception: 151 | files = [] 152 | files = filter(lambda x: x.endswith('.bf2demo'), files) 153 | files.sort() 154 | 155 | # store the new file 156 | ftp.storbinary('STOR '+fn, demof) 157 | 158 | demof.close() 159 | 160 | try: 161 | # delete local file 162 | os.unlink(path) 163 | except OSError: 164 | # couldn't unlink local file, what to do? 165 | pass 166 | 167 | # handle rotation 168 | while len(files) + 1 > file_limit: 169 | # dep: nb: this relies on the data formatting in the bf2demo filenames 170 | # if you have other 171 | #print 'deleting %s' % (files[0]) 172 | ftp.delete(files[0]) 173 | del files[0] 174 | 175 | # bye bye 176 | ftp.quit() 177 | 178 | except Exception, detail: 179 | import traceback 180 | log = open('rotate_demo_err.txt', 'w') 181 | ex = sys.exc_info() 182 | traceback.print_exception(ex[0], ex[1], ex[2], 16, log) 183 | log.write('\n') 184 | log.close() 185 | sys.exit(1) 186 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/mods/bf2/settings/modmanager.con: -------------------------------------------------------------------------------- 1 | # 2 | # Multiplay, ModManager 3 | # 4 | modmanager.autoSave 1 5 | modmanager.banManagerModule "mm_banmanager" 6 | modmanager.debugEnable 0 7 | modmanager.debugFile "modmanager_debug.log" 8 | modmanager.homeGuess "C:/Documents and Settings/Administrator/My Documents/Battlefield 2/" 9 | modmanager.logAppend 0 10 | modmanager.logAutoFlush 1 11 | modmanager.logDateFormat "[%Y-%m-%d %H:%M:%S] " 12 | modmanager.logLevel 2 13 | modmanager.logModule "mm_logger" 14 | modmanager.moduleBase "modules" 15 | modmanager.rconModule "mm_rcon" 16 | 17 | # Modules 18 | #modmanager.loadModule "mm_tk_punish" 19 | #modmanager.loadModule "mm_kicker" 20 | #modmanager.loadModule "mm_announcer" 21 | modmanager.loadModule "mm_bf2cc" 22 | modmanager.loadModule "mm_autobalance" 23 | #modmanager.loadModule "mm_reserver" 24 | modmanager.loadModule "mm_iga" 25 | 26 | # 27 | # ModManager Announcer 28 | # 29 | #mm_announcer.addTimedMessage "30:300:Server Rules: No team killing, no stats padding, keep the teams balanced and play fair!" 30 | 31 | # 32 | # ModManager Team autobalance 33 | # 34 | mm_autobalance.allowCommander 0 35 | mm_autobalance.allowSquadLeader 0 36 | mm_autobalance.allowSquadMember 0 37 | mm_autobalance.roundSwitch 1 38 | 39 | # 40 | # BF2CC for ModManager 41 | # 42 | mm_bf2cc.chatBufferSize 50 43 | mm_bf2cc.serverChatFormat "[Admin: %s] %s" 44 | 45 | # 46 | # ModManager Logger 47 | # 48 | mm_logger.logAppend 0 49 | mm_logger.logAutoFlush 1 50 | mm_logger.logFilename "modmanager.log" 51 | 52 | # 53 | # ModManager Player Kicker 54 | # 55 | mm_kicker.banLimit 1 56 | mm_kicker.banPeriod "Round" 57 | mm_kicker.banWordReason "Using bad / racist language" 58 | mm_kicker.enableChatChecks 0 59 | mm_kicker.idleIgnoreNotStarted 1 60 | mm_kicker.idleLimit 0 61 | mm_kicker.initDelay 60 62 | mm_kicker.kickDelay 5 63 | mm_kicker.kickLimit 3 64 | mm_kicker.kickMessage "Sorry '%s' your are being kicked ( %s )" 65 | mm_kicker.kickType 1 66 | mm_kicker.kickWordReason "Using bad / racist language" 67 | mm_kicker.maxPing 0 68 | mm_kicker.minPing 0 69 | mm_kicker.negScoreKick 0 70 | mm_kicker.pingLimit 0 71 | mm_kicker.positionDelay 120 72 | mm_kicker.samplePeriod 120 73 | mm_kicker.sampleRate 120 74 | mm_kicker.warnWordMessage "WARNING: Please refrain from using bad / racist language on this server '%s'" 75 | mm_kicker.chatSpamLimit 100 76 | mm_kicker.chatSpamPeriod 1 77 | 78 | # 79 | # ModManager Rcon 80 | # 81 | mm_rcon.allowBatching 1 82 | mm_rcon.basicAuthLevel 50 83 | mm_rcon.enableLinger 0 84 | mm_rcon.lingerFor 1 85 | mm_rcon.logCommands 0 86 | mm_rcon.loginMessage "" 87 | mm_rcon.logoutMessage "" 88 | mm_rcon.rconBasicPassword "{{rcon_password}}" 89 | mm_rcon.rconIp "0.0.0.0" 90 | mm_rcon.rconListenQueue 1 91 | mm_rcon.rconPassword "{{rcon_password}}" 92 | mm_rcon.rconPort 4711 93 | mm_rcon.reuseAddress 1 94 | mm_rcon.superAuthLevel 100 95 | mm_rcon.defaultGametype "gpm_cq" 96 | mm_rcon.advancedMapSizeValidation 0 97 | 98 | # 99 | # ModManager Reserver 100 | # 101 | mm_reserver.kickDelay 5 102 | mm_reserver.kickReason "Reserved slots reached" 103 | mm_reserver.kickMode 2 104 | mm_reserver.kickType 1 105 | mm_reserver.privatePassword "" 106 | mm_reserver.reservedSlots 1 107 | 108 | # 109 | # ModManager Team kill punisher 110 | # 111 | mm_tk_punish.announcePunishments 1 112 | mm_tk_punish.banMessageDelay 5 113 | mm_tk_punish.bannedBy "ModManager Team Kill Punisher" 114 | mm_tk_punish.banPeriod "Round" 115 | mm_tk_punish.banReason "Team killing" 116 | mm_tk_punish.forgiveMessage "TKPUNISH: %s forgives %s for a teamkill (%s has %d punishes and %d forgives)" 117 | mm_tk_punish.punishMessage "TKPUNISH: %s punishes %s for a teamkill (%s has %d punishes and %d forgives)" 118 | mm_tk_punish.punishTime 20 119 | 120 | # 121 | # ModManager BanManager 122 | # 123 | mm_banmanager.banFilename "mm_bans.xml" 124 | mm_banmanager.banMessage "%s you are being banned (reason: %s)" 125 | mm_banmanager.defaultBanAddress "N/A" 126 | mm_banmanager.defaultBanCdKeyHash "N/A" 127 | mm_banmanager.defaultBanDelay 5 128 | mm_banmanager.defaultBanMethod "Key" 129 | mm_banmanager.defaultBanNick "N/A" 130 | mm_banmanager.defaultBanPeriod "Perm" 131 | mm_banmanager.defaultBanReason "Unknown" 132 | mm_banmanager.defaultKickDelay 5 133 | mm_banmanager.defaultKickReason "Unknown" 134 | mm_banmanager.defaultUnBanReason "Unknown" 135 | mm_banmanager.kickMessage "%s you are being kicked (reason: %s)" 136 | mm_banmanager.dateTimeFormat "%d/%m/%Y %H:%M:%S %Z" 137 | mm_banmanager.oldDateTimeFormat "%a %b %d %H:%M:%S %Y" 138 | 139 | # 140 | # In Game Admin v1.6 141 | # 142 | mm_iga.addAdmin "d975d59a9b32e9f105a15667a18e93d7:all" 143 | mm_iga.authLevel 100 144 | mm_iga.addCmdBinding "k|kick:iga kick" 145 | mm_iga.addCmdBinding "b|ban:iga ban" 146 | mm_iga.addCmdBinding "m|map:map" 147 | mm_iga.addCmdBinding "s|say:exec game.sayAll" 148 | mm_iga.addCmdBinding "sw|switch:bf2cc switchplayer" 149 | mm_iga.addCmdBinding "w|warn:iga warn" 150 | mm_iga.addCmdBinding "r|restart:admin.restartMap" 151 | mm_iga.addCmdBinding "n|next:admin.runNextLevel" 152 | mm_iga.addCmdBinding "l|list:admin.listPlayers" 153 | mm_iga.addCmdBinding "p|pause:exec gameLogic.togglePause" 154 | mm_iga.cmdPrefix "!" 155 | mm_iga.notAdminMessage "Sorry %s you are not registered as an admin!" 156 | mm_iga.notAuthedMessage "Sorry %s you are not permitted use the command %s" 157 | mm_iga.warningAction "Warning" 158 | mm_iga.warningPrefix "[%admin%] " 159 | mm_iga.addWarning "tk|team killing:%action% %player% stop Team Killing!" 160 | mm_iga.addWarning "lang|language:%action% %player% stop using Bad Language!" 161 | mm_iga.addWarning "vh|vehicle whore:%action% %player% stop Stealing/Shooting Teammates Vehicles!" 162 | mm_iga.addWarning "hp|high ping:%action% %player% High Ping!" 163 | mm_iga.addWarning "spam|spamming:%action% %player% stop Spamming Messages!" 164 | mm_iga.addWarning "i|idle:%action% %player% for being Idle!" 165 | mm_iga.addWarning "sp|Cheating Stats:%action% %player% stop Stat Padding!" 166 | mm_iga.addWarning "mr|make room for admin:%action% %player% to make room for an Admin" 167 | mm_iga.addWarning "nv|name violation:%action% %player% for Name Violation!" 168 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/mods/bf2/settings/serversettings.con: -------------------------------------------------------------------------------- 1 | sv.serverName "{{server_name}}" 2 | sv.password "" 3 | sv.internet 1 4 | sv.serverIP "0.0.0.0" 5 | sv.serverPort {{server_port}} 6 | sv.welcomeMessage "" 7 | sv.punkBuster 1 8 | sv.allowFreeCam 1 9 | sv.allowExternalViews 1 10 | sv.allowNoseCam 1 11 | sv.hitIndicator 1 12 | sv.maxPlayers {{max_players}} 13 | sv.numPlayersNeededToStart 2 14 | sv.notEnoughPlayersRestartDelay 15 15 | sv.startDelay 15 16 | sv.endDelay 15 17 | sv.spawnTime 15 18 | sv.manDownTime 15 19 | sv.endOfRoundDelay 15 20 | sv.ticketRatio 100 21 | sv.roundsPerMap 3 22 | sv.timeLimit 0 23 | sv.scoreLimit 0 24 | sv.soldierFriendlyFire 100 25 | sv.vehicleFriendlyFire 100 26 | sv.soldierSplashFriendlyFire 100 27 | sv.vehicleSplashFriendlyFire 100 28 | sv.tkPunishEnabled 0 29 | sv.tkNumPunishToKick 0 30 | sv.tkPunishByDefault 0 31 | sv.votingEnabled 1 32 | sv.voteTime 90 33 | sv.minPlayersForVoting 2 34 | sv.teamVoteOnly 0 35 | sv.gameSpyPort {{gamespy_port}} 36 | sv.allowNATNegotiation 0 37 | sv.interfaceIP "0.0.0.0" 38 | sv.autoRecord 0 39 | sv.demoIndexURL {{demos_url}} 40 | sv.demoDownloadURL {{demos_url}} 41 | sv.autoDemoHook "adminutils/demo/rotate_demo.py" 42 | sv.demoQuality 10 43 | sv.adminScript "modmanager" 44 | sv.timeBeforeRestartMap 5 45 | sv.autoBalanceTeam 0 46 | sv.teamRatioPercent 100 47 | sv.voipEnabled 1 48 | sv.voipQuality 5 49 | sv.voipServerRemote 0 50 | sv.voipServerRemoteIP "" 51 | sv.voipServerPort 55126 52 | sv.voipBFClientPort 55123 53 | sv.voipBFServerPort 55124 54 | sv.voipSharedPassword "" 55 | sv.useGlobalRank 0 56 | sv.useGlobalUnlocks 1 57 | sv.sponsorText "" 58 | sv.sponsorLogoURL "" 59 | sv.communityLogoURL "" 60 | sv.radioSpamInterval 6 61 | sv.radioMaxSpamFlagCount 6 62 | sv.radioBlockedDurationTime 30 63 | sv.numReservedSlots 0 64 | sv.friendlyFireWithMines 0 65 | sv.coopBotCount 32 66 | sv.coopBotDifficulty 50 67 | sv.coopBotRatio 50 68 | sv.noVehicles 0 69 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/dll/ka001393.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/dll/ka001393.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/dll/kc002306.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/dll/kc002306.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/dll/ks001800.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/dll/ks001800.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/htm/ka001393.htm: -------------------------------------------------------------------------------- 1 |

S4AB6 7B60 A39581C11751DD5410FC091DDFA46211 72E9E64E78509A01B56671C0BA36C9AF r:\_bf2\pb\dll\ka001393.so

78DAEDBD093C94DFDB3F3E330CB28D3D2D4229A464B217310C662CA5BDB4489628BB1949120D3131

4569D1AE3D29DA68B56769B5558A90C40C15226BCCEF9C7BEED1F0EDF37C9FE7FF7F9EFFEBFF7BBD

9E79B95DF7759DED3AD7795FD739E7BEEF997BAF8DA32D0E8BC5F03F42982518C86529F0784B54AE

461FCB026426183CF83F05A38CE4C563FEF9F3CD7E3CC5606490FFB09C083C7146C5CE32E368FC2C

1E4B593FBE1C0E2DA78796D343F3F36914DA153E15E357FF95E6210C68840D8F9F480DB0E3A9305A

6E39282782F9CF7FF8ED5983C3011CF3D073D81D5D7090A02DC14106873A9A772E38CCC03115E54D

C061070E0D817A0DC1A1030EEDBFB4290D8E69E0D00207EC8E39389680633238E6A3796683830A0E

DB0965ADC0613F41367D02BF6002AF89522370CC04873E381404D229E098818E982938E4C16181F9

FFF63345E05C111C9213D2E526F04A02E7CAFF50A7CA5F645202E7B2E05045CFE7404C826321CA2F

028731C417CA2F0607013D2762C6FBDABFFB08019495C9FD4D2E3EE6B7E3E512186F9BBFC92531A1

7F954B8DF9C578B93426CBF66F72C21FFF1D2797C128FF552E8BD1FAAB1C8CD4CABFC99530E743C6

CB2D70700CC531E751B0B250F963DCF8B15D8DD23B185EFE2D8A3C7E122A97C7F2E45D2800F8E353

881D5F8F2EDF0FD1FC5B26FFF12BF8798ECA2B5027E6C7445F54CF0A143CF3D0F8721495F301D382

167837A1DD2A347F0A5ABFF3943F31027E6EA3F2F3A87C3E9AFF03DADF24B4BFBBD0FC2E084E1531

CEA83DF9F12A04AD2710057F9040D495FB0BDEE64EB03307A527D0769D15C7DBC154881FC1C77F62

F8F647DB5D81CA170A8DAFBF1EA52FD0FA0315C7FBEDC109FA78F1E33EDA5FCA84FEEA4CA87F264A

FBF8FAA31507A0F265E878E9A1E3C8778F77A8DC190D8AEEA8FDDD903A813F5AF1F8F3687E1BB4BF

4993FFC45FA41F423C79161AE013D07A06D0FAF3B578FC0F343F1EAD271FD5933F97FCE0DB13C5E1

49546E8BDAC172821D5C9171E9E6C64FF02F366A0799097E315DC056F053C6F747B47E35D47FA7A9

F1E489A81D9CAD793C13ED57F184187C0195DF9B308E3F51EA3F217FA340DC96FB4B7C9B8DDA416F

F2F8394B01076DA584B1BC2E83C616D40EA89DA3D0F15D86CAD7A2F534A1F53051F9BA7FB0DB0E54

4EB93E5EBE091D5F4B1D1EBF1DAD6F268E679F26328FB7E3FBD38438908FD21B13EC5084D22B7C5C

4DF9B326C0A06B06B5BFF85D19DADF2E34802D47EDAF31C12F9EA0F23004638A9840B4BFFCDEEDF8

8738F9896FB709F110FB0FFA10517D9AA6F1F8A7FC0457D76D7E01FEAE2134B7609AAB2BC6D5C7DF

878671F5020424B987B9C153375F9F704F8CAB7DA8EB0ACF6D3E2134CF606B5FB79010CF104CA01B

6D2BDD0B25BE9EFEE859B067103CF3A7FB615C5DF469C15ECEEE3E2B5DC331A121FE81C13EFE342F

207631DEE6E91FECE6EF116A63E8E7B64D8F08646322704ED40BA10583BFB5C16E81CEEE2B5D313C

969FE2EE8DA6B863781C48300DDCBA2AC03760A767B03BE04C02B7523CC368013EA071C01A81C460

3777C0B88F7121885EBCBC2B69C1A003CEEEBC7A00E7EF1EB80B36EC33968E0AFEF06E3494E797F0

0BE49720EAF1443E63321A4FB5B1DCFC943FD58DF1BC0CE1020A18066E750AF0F0F1F9531AF41AC9

4D0BA0070622D51A076E25F9FA06B8FB61FCDC20E575D336D8D313F40A8C09184297850BDD03FC02

E9344F67371A18477F6B6F4FF71D21743F67D3101A2A8263A3EFED19E66DA3CFB337C2402BC301E6

9B2E0C98CE8FA73AE0F41642DE9BDF6F7418FD50A5808ABE8861897A6E1E1ECEC101EE9E2121CE46

0030616EC148CB3C11C6CFD30FF419665C18E8161CC25752403967500FC815E24943E1E946F30C83

8845D5F2006AF88CB58496116C69AC8F44031FBFC08060BEE48F3EEEA891EDF8580C41461A217E81

08813ABA6E0B0B7305860F0980FE41DBE51AAA87F1F5D9EAAE1B12A06B8471F5F470A3B9815C5B43

4278CE0544FE1E183B47AA95B5EB42DD85BA86FFB8FEC4825D1876ECFFF8333EC7CF85FB97D48939

E17FDC5FF3E0FEA5265EFCE0E980C52C155853D0A7FA4C82BBC34034A08B398F9FE79AD038F760F9

78F9D87A73E5DFE56213E46A68BD59683D2213F61AF902F25982F15640AE2B20AF10901305E4B502

F28502F22601B9A9E07C2D20B716907709C80597EE030272AA803C2AE4CFFE7C86E09E68054F2E3A

61CFA326209716906B09C80567423D01B9E056C54440AE2820B714904F16DC630AC805F76BCE0272

C17DEC3A01B9E0FCB34540AE2E20F716900BAE770205E482FBF33001B9A6A03D05E45A02F27801F9

3C017992807CBE80FC84805C4F407E5E402E889F6B027203017996805CD0C31F08C88D04F12C2037

11C4B3807C91209E05E48B05F12C2017DCE73609C849827816900B2EEDBA04E4768278607C136337

02D4B2EDC14A82BD7A2114158B15710D675AC860B8B335C0FF24F0E1CE86BC37B2C46AE282CFECA9

908770E75420BC1CE4E165264E3EC28B431E46154E16C2E3200F2F0F71CE23FCD012C0C3E50D2709

E17F421EBA15270AE1DB210FD5E504227C33E46138E16C41F88F9087DB518E33C257415E1CF29608

FF1CF21290D743F842C8C3CB191C35847F0479E8921C1984BF037969644B86F0E99087971A385DA3

90BF007919A4FF087F12F2B248FF11FE30E4E590FE23FC01C8CB23FD47F87D905740FA8FF0E19057

44FA8FF0C1905742FA8FF0DB213F19E93FC26F85BC32D27F847781FC14A4FF08BF02F25391FE23BC

3DE4A721FD073C1C674A02DEDC5C064389CDA7E1B815C83027F13F51E6E59B41E75653985FE98B29

0CF30E33C0D1A65112CCE5403D6C295063871485912F4661E2C580845B056AE3C2DA126428B155F4

96127C3CA8005B5EE4E59504FE56833AB88B401DA042DA5490F7AE19AF65690ACBBC0D24509945E5

24EE977254058E0E680114440A833F4B426E1FE7EA2822837FCC212079C3B2CA339BB2538465A546

C82D639615FDC94FC826C494D3E463FB4884B3058473F936C47C621559AC8C3E8F5845615614510A

9A852984DC020A6B15AE82C4CDA7BAE713B225454199CF64D3125000E4A5BD63912ACC268556B348

7966F89D04C132E51DCF59A473407A47507ABDE31EDA3EB11CB418658161D972A9CC419A0C855B10

6546C0D0FB1D5992042AB38C72C2513B1FF43F528C142F5B422660313DA8F6BCC38658C52BBF944B

93013C955B681565361943EF23332BB40B62CB99659114D8C4220C4D0A502A371FA4E3403AE504D5

34DF9160976F139B6FC32C8854B667494E26F611B271DC120AB7301E0B54219BBEA6FFA0B2844DAC

4A846530453D4549486334650A73886F3F2B90B130269FFE0BB4370072FCD10EFEC55645EA12B2A5

9915C06293011F4FA0704BB965B4C984ECA5A060494C39FD1BB7AC441403C4B1F97B7B7B8AC6F78F

902D02DA8AC9A729F1EA8BC711B26D618BB06C3EBD8BC21C28C161FEB4DA21042A022AD207404950

4C265E9457A0142D009A2A1AA7DF5E21627E11C86C221AA1EB95B4578358CEB2E29A16D0386442B6

1D16162699E447C8730B78750026B2E1018C5F450FA0BF178DEF2F85E9ABD1E4C8F4D0605398BF1C

010334DCA8514B65FE7664D23406803B69C55691986FF7AA12F32909D3352809BE1A628E091E1A32

8EE04CD93181A6A1063C4CABC886D8076B251172CD34D85EBFB95C42EE1E0CB388BD019C9A1645C8

ED9D4CE29650B9C514EE331BD35FF4F71D99883A1DE9886E1D17517D4027446D40BFC5410FCDB053

F2FB2B405FA5C160CCE4A5934CCAE82A484F41DF4046DA7B900C7A0AC6A585D74B12CB328FC47256

339B02E04D26D816B296837382ED7350344238B2A8A316691835C1C4F147FC6B7CEB4A3155342993

E7340593E748C9BD3ABC866001907F190F16B497BC9C5F803220735B472592DE5334115F14E660E4

5CDE60CF02F81005A307478A45C66229A6A53409DEF0C3B1FF02C79EC27C0309BF1662556C1F7380

2694DA3F0ABC9D377EB570FC9AC0F8C1A1AB8083C846C7AFCB9119810CA20925B68F868F5A82A1CF

7164365112366A88C191948123A90C47520D8EA496634284861E184E13D06C3FBB6290378A246629

9B06CE4951661A8029A107504A8591C50D4C657306409249094D1A49271372D98ECC363AA85A43A3

E370D46E2C86C2DA8DA57591993FD8B74056101C28CC225A1399394461FEA080BA0740DDB1F9F41A

0A0394A789504CA76B84165099F5B0BF1C276071B4CE0E19AF247E7D8498309800EA7419AB9310E3

81C8F8F59EE7D5FB92C29CAEC1598EC4DD24CE868171F1979330388E774C984CC85B26E7C8ACA014

348951F4EDC42884BC4A12B31CB0421462058565108BAC2708B97632146209C53D4E01A08152D03A

C991F88E4F805C3F4719CAF573D400892D8F5C088219F437428E9D0285F91384664A41BB3285518A

8B6EE6C20B47058CCF4294EA560A52DCC6B400D883D9C568E23A126CC09856A0F3CD4A30AD38AF83

D3918A3A24A578A575BC150FFB763F9CFE0666509885A1D294D83A30AB311B3770D2A17D58F86930

776C9D57A9304E1D43FFBA01AD8FB49A984F62FE22B19CB8A4550035AB5702B45060CC24F0F4E5E6

83611506F197F94AFB59ECF7480B300B520184986D341F385F55E4C24B7B8C2682198159B0539215

A4C722AD8D1A9C44B0EBB28C3786C82E6817A6D73287C9DA45B1757B09005D940D9B481B499B104C

735F803104634B712FE2BE22E42A08B3C3413762EB08E70A63BF83E92AA6825EDFF1021602994056

5ED13B7A9C95FDC878C2F90F9D4FA8CC219A2C6FBE1005FA2E6529889299F9CCD7DA25B17D910B99

95A48750D37C8E70D420215483FBDA265E9CB6D09E6520CA682620495143040A2B121BAA114F28B1

C581898BCCA2A2BD2113ECCA9012F4D7562564512CA68865AFC7B25A1B35043A5AC6CBDFF11AE8C3

6803C656DC1F268329C54F5F2383EC93284C733A103C84E7090BB9053E25AFA4008A9956621446A1

58F46020584DD25B4BF1DF56CB60E079D1F878C45A5E1135389F90920F1B3B5648EC13ABA0E90309

4D91C212FE858091A510C89BAA69B80A5E442A45A678909D5754AC90FE0AC0A9088C371CE65E30CC

CC61DE5A69991805C0D3DC3181E098602703561109D9924053627E87388C37D0B7C2C4A884CC7C2A

CB0C44A81C984888B9059A05939A7B21C5B42CE2A3536C0B21E631D42421449962FA3AD28C92B052

C691D94A61BEA710320729428BD956BF000E13ACC51E2126E1E6233E6B2D46886160786B7166058A

0B904B8DA5604401EB15C70A47F776476627A864B70CFB5C2FEAEACF68CB2909FA30A64D810D0167

0719568AB11B608684C5EC6CA42D07B088D37164EE50E6F803E7275621F1899602D4627BC20CA04D

6215AF4DB87202EC2318DFC724C87CB696B486CA1C856603368BFEB60EAC6429B15C424A21689D02

42AC18625D42EC7361D8FB25302D26179EB36234F440E6121E310394086820A00B015D02A8BE2202

8E120A5358835DDD83F68DAC214388F98ED400C2F223319089085AEA6427239DDBA821534AD6D080

4624644B80B94A827315312898A924A206D58066D0FB90216F8F1A94A27D8D1A14A735450DCEE4CD

5731D160AF02F31D2D8C29271C050E468889434493083191C8891421860E4E6C882DA02821C6079C

93B8C554F76242B681B265D4282C6B19D38714A6C901C1249A24F82F451301FFC5E9D536000D078D

4029329C5848D12330BE110E5A00C92378FF9CD8E708669742D8E104601D49D85515A9D9D0B0F914

02B988906BC21606A9A4785B194AC2130D1349C4E6206998FD0429F544C312C8A84C11544CEC635F

FCC94BA02009B6627F529820C50BACA31F44C13D64E97E18AF911540F46034865ABA5F19BD66044E

D5D053826381576CBE1789692BC72C201CA400B123EB32D4D489F9CB86C8ED88A426086B00750931

09307098E810620E8393E821A4AB7195C08BC77214E3608EB9849817B83F395E2039CC347878C141

A3B09BBA419F09394BE540C3360A09648DE960EAA1724BA9009954C6331C37DFB4907010BA1998E3

C860669F4E668E320AF1689DFBBDB948A42F9AC5D32816A29E97466266C35E13E2B620CD82651C88

B0312B7118642E054D7B76C3A2F7342C15A1F5109380C90360140AE6014A825865EB762320A5C951

5861B018E4417668364471A83350D88B546AA3A086FBA337E80750FD900D5490F144430B6E4F6906

442E007642A2AE1832BE605743C88E311753807E94A22B063C205E58164E208C12314A341786527A

0505AC5000280C344002FB5C17A20E214619F624611558D4307164A08406D823D0C87F9A32854D21

F8FFAFB68740D806CC7B4614D619581078855542C465B1582EB042C262307E3471B62DB0030FEC2C

B20696C22812032B5DB2293BA286125D0C4306B262E40516D266926B11DF5D670AB861EC1B0CEA74

3309B1D5BFF935A2EE73A80048A8EEBDC0B9A8CC46B03CA330BB09F1F07624D5FD2D39C15143C391

F9A406769744C81626D8C496C7C7286881CED1C4C166C894E78B54F767146C0F89FB8CC27C52AC25

0C43488C30CC44068B463510DE19755CCEE56160559EDAC47C419D59786F751E94630D4780FFF046

99B3720462C7DC11A4718C9073154B789E06CE4177F8A1435839261FF4D66C26D25B3A1B4689431F

415B1C09080B64F412A602D4B1D77502FE04050B2602321C52E5BF6903BC90580E672700E421B8F1

673602BBB0337E8053F7016028B0CEE67C1E1D5FF3E88F0935735E8FF0B702BC03CE89DC373431E4

AA01E0A3410D00075DA083B797C960BC887D8F92A0270877229D1E592A83E18BBA41E551E66478F5

102CCBF62EE52DCB282C437D354412CB9794E2B78F254A319622ABB82D8024F1123DC129BC7EF408

5E5FA54B77E8C1AD2DD8E216709FD3C4C0EE800BB606DF4AC002224151691E28CC326F03B3197025

A032C7EC3BD44BF1B12AACDC7C0EA8EA21AC36173E4640CC677F07C91D2502EB8B5CD8BD3CE4F216

16AE90496BE07AB00F99EF28CC9195CC6A126B0DBCAA6209D784126013C8CD8F32D3021B7E12B386

A45D0C4631723D631843732324D621D3591B85F939779B9717D24612B3B6FF07B3B66040B56008A7

5DC8127E8B3C8C21ECAC5DC4B2D6635623BBF6D82A7A2D89F98AA43D6203D77C851019C8CACF1C74

C9923FE645700E7CF50D8E742189653B0AC651BB025E69F8584256C4E8817F58CCB8FC3093A37B17

890B563E9D00F984DCE9C2EC445E058ECC2EB4F4A4F88D8A18A43089FB7242FBC5FFD2BED35FDA17

87EDCFE757D15F4788ED1F85C8FFCE99FFE75ACF9FFDF646B855F385FBED61B86FEB82FB35649F0D

672CE6AF87706AE22D2F90491C5E9BE2EFD698657FF66BC8A69B92A00496E37B4591421DEF288C48

314CD84F428EAD1C98BE22395E8CFCA84811AF525BB928B8320373625FA43DD83CDB80CD993CB1DC

31C141062CF364B90514F7028A69D71EDD3FABBD02B0DAC3914DEC64E89F29A693E9F5C8C40DF655

ECDD1D006BE59C6DD06763BFD362C1320BACF8BAF7AEE6ADDA26514C0B26ACDA38ED4894A6328BC1

9483B8FA5F566E1A1DBC95DB6F848688393267C1951B2F0F5C438EC24D1D58460EB7F396910F272C

2363DF02A3737A46C7591CACDFD6002833DFF2F736D1DF9CB17F59C011BF0343CB8130035399EC3F

43CE02CB2DE0DFC8AAE5BB238B068346235B142809A04257EDF0A3301B908D49868609BCB8BCA71D

6E39017A08D9F9D8678ECC3E98BD9683EE6FFFA57E8887C6B1C281B06B487D14668C861914CDFF5B

7DF1BCFA8AFF565F82543A55062C5654847C6530D1830138B8F26A834B93C120781EA30AD7320978

052D1839A43EED00F10BD8134B49582EC62D055B512E772FBC15164599540180CE68C2522ADB29FA

C9F08A32555F01D85A96C228108BE67E81C1A984F42811D4D7214466BE21B3C0BEC3BD9F755C05D4

414D90ECA10250B95781655051F46758E36C58376552018559C6F80CAA6553F585ABC15146E23EA7

32857BE88F2809F8E248B8B755B4817A9597E23F507881F21F1553DCBD5306554C311E96752FA026

2814B1485826FE3C284C811B79B604DCB9330B2905CDA2D1CD516BE69DA1100B91942602A8B3A493

9FDC2E07CE8A400C2ED82E83C1CB62C0266E2D85B789F3028073647E2427C8710B483E857FD9C1B5

94E2E752783B38CE493E0E79DE0D9D9ECA1C41FC1E717A0044653CD8368F827DD87B2AB38C10F30C

AECF0EB2C0FFE8D1FB30FAEBD1E1703942F1A16C2CCCBB0AEE0763BE8AC0F3D5684821C4884E02AB

5210B7620E08C3B59D0DE6D17024FDC4C3BA28C24DD84BD3E710EAE637121CE610B29F1362724055

315584D87BB0E26CC59D7632809807010202037E1BC21ABA22446AAD1DDCC6EE5A2F833CBAC8AC60

749D8EC76F07F8620E30FABD185D3E96F1E69B010B882B20F1F865300D6463C4E36DD0D3D478BC19

38053B7146D79D783C118ABB185D31F1784D785AC6E84A8AC7ABA0A74DF178795E86BE144657B465

BCA138522F978254F6F304A32B2E5EB1178E2B4BF13305267DA5209593185DC38CAE5C723CBE0E11

5752901ABB9B195DBFE2A54A10591E2C97705A6300D9AA6568C0874E000A1C132E6988E1A1576468

C8405174311C1BDEF53DC6E832424C04E87DA40B637439B4370D19B7157CFBC746216B0CFC290DE4

1E41C759701E879E1F86EB876C38986A4BC1428C113D9A0391F4285E9E104B1D850B8F24CE0E006C

C6289610D3240CEB7522C42E00298F82402BC8F52F0764EBCA2103F210DE75620C62294CF3677080

628284208F23C4F480DC4CC3F34018DDA7C935907364E24FD9416728A1981BD51AC811625EC23E9B

1B23E79F45E0B92172AE8445160AF7D7CA601E6165E03684F404E287F89DADDE0A3BA6F271163470

1399D99ACF1666391BB3287A66E2CC2E825525A389C01898043A73501868C66825B06C8D415D3EA0

AE786B6C89350E58B781FB8C5AF04D9C4D0695314684E8F729098662503530BDC56C15451A370105

D8FA5F914817036F5239B21235B280C25EA5F8A9204D0F6EA2ABE0922817DA310FDE6FA53E78F592

F7A180A99CFB86DA0F66D54A4A014788C2127EC55B5B0953B44B985DACC0B52C673DA028C10E4C90

5C150AF3757C8A19AC9E5E4C6505A9000572D60005F6B5202BA71BE09CF30EAC0A81376C2023CEB1

1221860E0891B22643A7F000D9E04D3A46975B3C7EAD2DCF25928183005F58668BACCADCD640D099

D9F2F00CD0791FE0D90049339C6D0BD3D46D79208D657431E3A5262332695BD80ABEDE1A241532BA

06183FBFC4E3876D4092622FF8CFAEFB0247C5B0D806B5E15969C486BDAB415AE7176843D089D6D5

101E3223E34CF966B5A029112BE6CA6BFE779912B9FB8760408FD38F98CF7CBA356237056B5E6C91

4058292184280E5B4133AA009DE0BD4881802207FB8A1706FF59B651F1F8DF643478E4C4E3BBC9BC

3091188F6F23F3C20B081E9FD053103CAA7919BA61F088977A418635E593790300630718807B8830

833C216A5C40C4277855FD6C8E574C2223412602101842F07BC10927176E8DC0E221F610BCC1912D

656AC5EBDA4240490FBD906B32E6B3AC906E4F4388A2BC1554C9865800B0418E975A001B8E97D243

9A53E5F7CD271EAF80F60D844E71F414844E2C1941168C9CFDD66391F3BBF558E7BF588F75FE039A

213A1EFFC69A6707245E4A155BC3C69E58F3AACA8DC7DFE3E5EC6333BA528049D291F4F3684DA0AF

C7D0D3E6787C225AAA2E1EBF8F27FDD9CAE802900C85851483AC112BAD0104D8529AD13F0C6CB9C9

1A0EADD54A19E4E94B30A44E40C0D93784EC9876007B20FB25621F3BEF33227A40FA23CAF88C84BB

D5434850D555450229C776084A294CA9B7A034C784571376A540CC7A7B27E7F85378579DBDFD3312

B66681A21D1A6341A966856050022109CFBE0D32725FD1B4FEE3E0B6F7090FD41D607562B8D40AF5

BAE578C4EB2241ADEC5D4DBCC8B566689CBBC1274726BA1B0C5FFF4DEE4642A118233C8440F19605

0F8A572D04A078C2028162124214E32DC641F1120981E265121CFD14D21F2832496350DC4B1A8362

28690C8ABEA431286E258D41711D690C8A4B496350249304A1688A3446248D41519B340E8AAA48BA

22690C8A12A4312862496350ECB7E441F137A3AB271EDF01384E20BCF290A0A8A102CE2723B73CCC

9B9C274C6A4FE1631A6C6A2302109CCABF9FD7E0DC3D06A164E709101267FB374208D11F0068E85B

A2D0A8E541C31DE4666F6EE04163F1C03868D838FF4F4E6A9C39836391185E7103A0386C8E008069

8E4C3ED72CE0BAB8AD147F6E19EF4A0385F985D1B5331E7F08C109DEDD1CC6BCE3F1F8280B9E9941

8CBC14AF48B78063E3C7CB6386E4E98EC76F063CDBF613DCB9B495903584486C6DC02448C9F31AA1

604B29EE95849863187817CCAB54540F4C4CF021692AE818B3926F036A01C782F4003E606513534E

93A6F47F2065CD84D7E8ACA9DC32B061A03C1AB34C7F3358A74383BC450DE2AC5DC02C03A3076C82

18E4B5557CA2303405B45D6C39FD29AC5212D463551283C8E7DBB3EE2127EC83F5887942CC10BB24

2D41ED12B794B7E6677489C6E3E1F32B208F9319ECF0703C7E077C4E840E3BCC303707E7A03FFB44

90EB3C814B91F2EC759F90E035052948C1C26BA5E3EFC6D810CB897D364440ABC0BED38BC21C2572

F9377446D01B3A61BC1B3A11F0860EF2E4C15F6EE8B01490C7A029CC01479624AF604193308559E2

C812C639B2144C28DAEFA8DA8314ED51AAF60089901D1B8F6CE2DBC8D8B2F8188D2664BF101B85DC

BE648B62BB80EC334F96C4934993B1F940D8CC1386F1844A646C05107E81F65B52C7E592599623A0

8736B1F991F389E5C0339865F0720F7B064B01CB2D60340B6917826D20213B460136A8672B0CFA42

362DA0D75298AD24EED12CB8B5AEE278F42223C1598400B5691132205C337440FA1CC703B5D50C19

93478B50A0BE371304EA4B3308D4625E9E238B50A0DE073CFBCC4701A0467D8440DD6EF67F0150BF

7E40CCF3DC14B14BDB62D42E0D0E0240AD5C8C74F892290AD442C0B35F7E44809AB21801AA0CB2E0

362C75E00135F32302D4C0C5FF0150E10DD63E1E50B7FD27813AFE7EA55805FDE53F2095CA048B55

0304A954ED0A8054005300D67F87541E2A27401581EF3F2335A55610A984837B47E065ACFF1C5A49

A665F45A8E136F4F6067822074890932129B16A123B1CA7E3C42C90890F1CA2628420D17092274EE

2288D099BC3CDDC62842E501CF1E7E2F80D086F710A165A6FF1720D4FA3D621E2D63C42E36A6A85D

E0DE7F0CA10B4C910EE38C5184AA019E3DB71641E84F1304A122C8EC69389BCA43A8542D82D0D726

3C843AB29CFF01A1442E00A88723F3DD7F5F1C45D189C0B282874C0ACBCC848FCEFF77D0C40E80EE

F1C1D9FF763C389B87FEF3E0444229278BB75EBE022C0BEF9EAE011532061C08315E83C8A8241BF2

E67F437454AED8F1D08AACF1528DC7A0D910AF78C8184273BF3132526B0CE148D5C4E377F2783B84

070B3C6FC0B32D412BB9083A586600458ACE40A85DF270B22672AB611F48E5B40E20EDCBF1DA9F64

888003C1085443D56EBCD31078AD7C31409D06632CE834BF8CA066DF8D903C4F0C50A769003CBBA4

46C069AED740A7396234CE698E083ACDFE7FE334123CA7E958F23FEA31B235886DFAF411A3C81BA1

4611B715F09811C470F80A7DD4637E009E3D5083784CBE21E231BEC85D5CC31E1B9EC7D4D5204838

6DC88FE9817F597C70C7161F20A6F713FBFEFDD3247F8DE9F7795ED305BCE6D79FD54729BAFAD0A3

68BFA76A7753B4FBA9DA5DFFA1D7F09DE1BFB8FAC8AF1AEF3637FAFFAB317D573F62AC00039EDBA8

56A16E53CB83ADCB4204B62B16A243134016709BAD06826EB3CE00B9FE69800C97EA42D46D2C79BC

CC42D46D8880674FAA1AEF36530C04DDC601A472D60D207ABDD7E7E9155789EAD58CECF5F0854444

AF474454AFF7D6027A3DD717D42B5F1FEA95A38FE8114744F5BAC6E3C388A85EA9806707558ED72B

5A5F50AFBB2095B39517662C50BD3A2B50BD227873E35C9E5E33F97A5908EAB5709C5E5A885EAA3C

3D3AF550BD64787C931EAA1716EAF5A162BC5E9C85827A4D837AFDFECDE5F29EAF466EF4ACA4B837

001850A2BFD5220075D4302164973932C91A9600E42CF4CE2BFCDA8C2348A350B40BE0D33AF0F63B

F733413BAC9BA01D08A87725417B4B01417BDD6D82B6731A419B924CD0B68C8639BB905A3F970A6B

C0E7D44BC91AC27A8804DE53669135D4908778D8DD406F5209793A175E51868A6C206D2C4A9012D7

43EECFB8AFF80FEFCFD0A491FB33E62BFE7F767F46C313B93F737939EFFE8CE9827F777FA66ED3D8

FD19B6C7F8FB339805FF8FEFCFA82FE7DF9FC9D11D777F2601C72DF0C9FFFBF37589BAE8DD99D3A3

7CB824310798A370FF0F22B8B331BAFFA724487D9594C1A09701642365C166BA8484C3B06C8DE349

58609EA231811E2A18FB7E01FC62C14AF44B0865E2A07F8C012E6D2A85A5F21830F079C70A4A82F5

0085D1210653E85DF0A6FD86F222E4B903B6EB1BDE536945B6B37D3D3018EC34A1C57A58DE6F08D4

802438942D805E02B41750F8DD5B0500B21A405300FD06680DA0F06A3C19B8C5744069801A007A0F

5047405B00F5005401849908403D004D01B41750F8DD6A5A3597FB12D01A405B603E309DFC065403

5005210C661598D5CD00F5007415A034407D015578C7E59E867240EF01AA019683F580DE03B41750

61B09093043EBC0A500D4063C086C611505A23971B03A84713280F7940EF015AF319B40F28F90BE8

0F58131AB4807E005AFC1594833C8838AB607F01A5010A9FFC8D11FAF39D2E6CB8F30A0C364C46E4

54EE3C19CC344951B1242CFFFBE9F0D1E5DA612E17F9CEBD95B44C228E2CADCC107292EEC222DF39

835FB55E07220AF3CFCFDE20F28DF06AE66B2E57F0F76CA09C06BFFB04E49326C8E177DBF5805C69

82FC129A7FD704F913F87D2D20379820AF81DFA702F28009F26FF0BB6A0007EA13E4C2406F9337FF

5ACF7420DF02E49A13E4F0EE761890CF9E2087F71E9380DC6AC26FBF780079199023DF75B74F14DE

8F5F2FAD7710C710610921E5E06F3698001C064DA8EF34909F07F21513E4F056241BC84D26C85F02

B90CC0ABE0F706E1D8B500B9189023BF65619F88DF2FB259DAF210EEA0104B98212A505E12E056A6

EA5FC74B03C8C5FE223783F7D780FCE404F92A1C0F2F7613E470E51508F223BF8F43929661E078FA

C5007A0DC89FA2F862E11CA4951385C8D26A24692DB2742003BF5F1896CF00F92C819FB126D40B1F

187C00E43613E4F540DE05E4CB26D8A317C8B5807F22BF1D02DAB382ADB170BCF6606376D27A100B

D3817FE8017F457E4B8497EFA0D0219C8DB49A95B416495A8F246D62279DA4C310DD2F92886709FF

E93F28C7FEC8E57E8502B2B4CC58FF813CBF0ECCFC02FAC0EF2A423F7C00E4CB517D0EE2ECA495AD

106DF6E3138559C0127A0C117BE940144F305EB0EBB95CE4B731ECF70B33F0CBA5F558B84421B4BE

1A7062D2C0E5FEC08CE9CD123A08F54E1426239ADB4947093144F6E351FC03D50740FE04EC78DC4E

07F240105F90DF56B067E059B855D2E7C51385F60B63FEF7F3BF9FFFFDFCEFE77F3F133E5CF4D334

FBEFF27FFDED07DE6729FA3B0BFCDF55E3FF3E14FFB701F8BFC7C30FBDEBB0E37F07E6BC3866ECF7

0790EFB7A3F5A1624C04CA4B4CF84D36FEEFF56056FCF93D40A43EDC9F7994FFFD708CC06F0668C9

8C97D74A4FF87D40ECF8DF05E3B7378A2ECB1E488FD92540D00E5D283F1D4D1F44F9FFF12967A5CC

7F4B357ACE7FAFA70F1D4F1179F47796503A07A54628B545E91A947AA13414A5FB517A0CA557509A

8DD21294BE43692B4AFB502A820EA8124AE7A0D408A5B6285D83522F9486A2743F4A8FA1F48AC2DF

EDA8F60F76983AD466911915DD60997766CAC7784D8F16234B13F1A65F2FD58B8FBD977DA91A3DAC

EB353C45BFF37862DBA4A7EEECA7BA2B760E31F62D3CB6FE2573FDFD33614B978ADD6AC1DF786BF7

24ADFFFE4317DD68252623A8B2AA51BE5474202DF064F69AF88B3BD43F7196F6DC9C99DBF7558813

F4E4B350C0F2FBC2AB44ABA4B0875C0352573C97F7D7991FFECD563928405C3DDEC141EEE49E5493

7B46272AF7B8B4D49D7DF77C792CE6B54809417B469FBC5D7C49CDF6D8859B8E5BEF98D44FDD6269

485DB3A8D5E7E94CDF53734E14AEEC90DEE79A11742B613AE3E8D2DDC5B65D2EEC275891F9834DE5

87E6762B92223E0E566AAD2EECF43EF915E7B3A986F636DA173F5769897AE69516471FE9BC9B5691

67AFA7880E4EA53F63F8BC896DBA41CEA97458196E2CFF6052ECDDFA6596D16EA2EAB3EE6FACF55F

92D91D797B71F4A77E210B77BB1B418F13FBDE5FB3DFE242F1D9F6396B40B5DE74F096E1DB9A6D19

12BF5B0C24AF4B58722C4ED16F2B6967A6EF9D13C9F9D6EF7ECAF8A8E6778DB000BB77399FB3BBD3

9F633FFB769EBD3310E918B6BCCE3273F7F55DBF3C128D9A76299D38BEF325D9BF8E32A9675DDCE3

D1A727B1C9D10603D1678236CF9A7C4EB8ED4EF0B2A347BB2329A62E79E792E8C9BA6FDE6CB269E6

1C91128A2F3493CC4EC9EFC3D8D96A68F916AF8AD13B31E0C44ADACEF6F821E24A4FABF870E9FAC3

81BDD5FEEB9ACECD943B202F12D275FBF72DB19423ED78D1E0F95FE55D9EEA56D89F15532993723A

E5903A2383253EE50EF1587B80DFA5A42BE4A9768728DB847E2E109FAD74455263F760589CFCE663

936FAFF238424C5FF773FBD9A3B54FDB2DFB99DDEF526A5F16C4F5EF7A32706EB78542BCAA4A64C6

B70EE394CD678506EE076B46DCF65B6E459667B4F99C568AE72EA0F58A52A25C37269CE696E84E66

35F43E0CFAAE4CF9198535737B7F64C9FE236917AF48332FF63A88F566B32F8B957DBC2B495B5479

E8789AEAFEC65BFB22C296C548356CD074BCE771089F43DC91BA7BF59DACF0EEB40F84E605994AF1

87FC4EEEBC94BC8FB444E3BBFAA16FAE2133C3FC777A360AD55B1BCF37D0DFF22DB0B18595AC15F0

31D245FDF8F9B8C24A934935B883B5A4E48F58FD83CD5A32D1BBE88925EFACC2E495924CBD5524CF

0B6FBC376F799E6CF839790511F7999924C7C860DFFD59DE83DE218F5B840FF507BDF67CB86BD2B5

6BA6AFB845598B8867BAB1738895419437F2BFEE9D3C15B064E0F0E16956ED8B2B598F18BA179CFA

4E1CEB4B117EEA78D27C437DC5CD65F6EF8B9E126D4F8EF4A5598648E40E49F94BC9B4DB1A756FF8

2024792F1D3B6B66FE1E3DE2ECB943CEABEAB9D6418D0D33C5ED93F797EE8AEFBEC73A7FF0995609

C6C3244EC7EBC6951A23D939E55E5DD24A927D1ECBE7CD7AF66BAB957C50A58E5CCBF40CFDCDB3E4

AB19B5FD778236BFFA667C8F9E85C948CF977A8EABF6DF7B69938DD682509972E98AA5A78873D6D4

33A77E9C74E1C4FB6976D32D3FF7A9781AF66F0F4B312E14AEC9FD78EF734564E0358FBDF49D86EA

54AB8F8AD26A2F7E1C77EF726E2FECEAC64855148A58967D3DF45DA42C45216CE6B923B3CF556725

4DD39A62201DFD435BEFD2A7AE757D2E577BB3EC256AFBE5164BECE815F2204E0BACD10C9BC5D979

E39DF9973BF75DC4BF8A1E3BAF1A27B5EBC786BDC9FD1BBB9D03E8F45B6AABDCC331AFB5CA0F2F8B

A8BDD89026A997B2828AEFF97C5CCA7F43B15EAE8896DC835E6D93A71DF737EE3CF641B8B0F8A9E3

9BE9994DDF4E0A07CF897CFDCE603002772B5CFABCF433F367AA72EB1AB7E9911C740E5CF6F6B761

D404EB7B7C7F716377DBF3AA15AD26AF6EDFB0E62E8A211C7833FCF6A450A67915CB795F80FF7DB5

F5AA4E87EFACDDD2C459F8F2562B5B412B6C5BB7D642FC8A13B77E6E2B1BCA518E70B03A29ED7CB1

3A35FF5E255789FE3C08E7A1E45BF7E5BEC456D72B2FCF7EB9F155C784B3C2A23FBC31B8F30B27F9

81F5ADF3217E1F2FF53A6FA014DFBD90192CFB992AD956FFFBD7E96FD3DB2D25EBCDBEEEA93549FD

B24E6747427BC08280B0E50C8D5D110F6CFC67DC3B59DA76D4B8AFD17379D0B23B564DC75DEDDC3E

0FDCD97F3A39FD4803E943C01ECD3BDE83D87789C68A62AA66191D47768ADE642BF7F42AEB1A4764

EF3C609D1298F94DAE704E9066B8FB117747CFF5C33F0C8DD6BA476E143176359D44297D6AD7A8A9

F958A7AEC531E8FE82348B0B11723DE4DE3BE91DED2FCD86B52FCEDE6C1BD7FB6BE7CB859B30FA9C

03C12DB8B74ECE97E9E6C74F9D578CE937D6F9499F192172C837C778C8F80ECDE3D5D1ED3F5B93EE

88FB370516CF5A5770B9A73452E8F28E74BCD1A9B58AD11DDDC1FB2C0CDE3D19743B2C43CCDD7CC6

38B599655BE6E29DFCF3FECEE5CDA56B851592E47FAE7EFF6BF6F924D303F7B61117370FA5679904

491BF4CB60AA43485BFB31D7D854F2AA8FED83F6B303665E9F478FDB9BE3A9DD7DADCE89F4F2F4CE

B2E111D1E82295F0207277E8CC25D8397D7E65E91D6B16CEA1CE5BE8BE9F54E8B55F6B9571838213

E162B09EC2B1194EC728EF8887A55F7E708ACD4AB4ABDF5EB73D8D1BBA22CAE1FEFDBE15221296BB

63E6BB370689776FBB5C9870D77A4B006ED33977FAD7C8250597F2DDAC26EBB7941FA9F84CF6FE15

324FB2FDF9D09BD4032EDBD336CA0F743CDE1ADEED187064C6CBCE396DDA0B82CE2D8BCE5323D78F

649C50DFBAC14CE27DCFCA9DE795842FE48F16ABAF3AB434B164B737BBF4D6D481031C9D86292105

4F961E9FFC41D96EE797C4E274856AF240F8F12F5ADB538264476E2DDF3AB4F2E2CFD6B410ED3DA2

DF8675DE2E68DBB38F13FC903C586678BBE3F3F629436DC4B81D9A0FFA6688BCDD35AD6EFBA1FE80

DE771BF54EF9DEF65FE66AB8DC087321B697F3400187F3D7AA095C3152E87633C774E54EE5BCD145

B64BE2A43AD246A22C1373D69777FEA41C527DC33E6413EEDCF029F1EC16460BA3E2725DB3F2E351

2F7559ACFAA9105280AEAEC99188C34B6E9CB2B832D879ED5E9D8C5BD739DB65BAB7B387B6FDD08D

F50CD49AA978F2C43D67EEAC9E202CBBD16ABEF44A8B2573B787DEBB581FB874DE1EBBE48CB9B10D

96CB9F1BAFCB2D2A90D63D7CF5F4EFFB754B779F8CFC26A7AAA1B7E229637BF7ED5F84AD8DEC3565

4BBBFBF29373CEC8D615DB64DC9C5E52A783559590ECA0C527AE3CF9B9505D2B4CF37052E7FE6FCC

63937F8E94CCFA297ACE5BE1BA94A6D9CC94F532CBD235871FAF3CC1DA50AA1DE237286537ADACEF

48C1B30B7B466A5E6717BCDBA36CD2C90E95D7AD6D585EB585FDF8C533C92DBA1581E67267745DA3

A3439AE7F7AC2D750FECB0D46E75118A484964675EFCFACB2963E1A318710DA30F1EA6FA1D3DA75F

11FBB956D7763434FBCFEA965722B779ADF62F9711322C33957954EFDC2CDC9AB5C7E360AC91DCAD

BA9F5687B60CAD3B3FD5FB6EC73D868FA3822E2EF563907475C8DA0B56DB6E78176FBEBF33B76191

ECE8ACEEA93388FEB14F5D9DF5F02F8E3FA5CDE815AE22DF317AE9B43FFA6182A8E62F55EB13AFCC

F77F30D6967568E51E0CCDFC58DD259CB9787EBF54E3FC1773DC9EFDB4E83C6779EFE2ABDB56F54D

CF76ADBFD6F98BF16BBEBD66CBA719AC29B5D39E9CEB72E95AF7A3FAACD38623FAAEBAE199A73475

DE7B9E3C6972EEFC20757A60E3433A21755EE60C7A18337DFA537BF7993537630969D19ED94F749C

CA7D1BB67FBF7C3362DBA5DD1727CFD94636F2726CB15B3C2F67B984FB6BE5778BAE4E1EB9BD7455

E257FA827AFAED139B8C4C4208A77CF7EF5CE7CA4E6B349D19B0882C7F45297DD87AD12976E52B8F

DAE39FEF0CC815551CF6DCE4D1BA9E1B7DC4F370F1879ABABD9BC2A30A544F544C75179F11BE4979

C7B3E4F25FDA2FC90B76F56AD70F6FAEF476A45DF0FB21B2F6B5ADEAFC0A31DAB167C71FDF3E15BE

C7C45DC7BE8EFED252D3EC53A7FDFDF4B7DD3D2F4F6C310CB2DF91277B2CAF5929B6C07A609759B8

F163B1B546535BD51AEFD3527BFDAFFEBC9BAF6EFBBC69E52C1DDBE6C307328CB7674CED666D5BF4

7B63DB0ED1C3718ED57ED3DF4F8FDAFCE2DCF516BB8439C1058ED5748F8E7C9C0EB5C1C9C1A4D7FD

795AB9439567606E96686FDDA56F6F72D2BFB1C45FA9A65D6D103354F27D647FF109EEB4DA9DAF17

66E81CF47CB99C9CD6AC92BCD5ACA8516CBDDCD3C4984DA9479C9CB05AC79BD633E48B83C8D302F7

3C6DD9DE8E7FFB10AF575D3B7DD3D1F70411A66B8BB57CC42C778229BB67EAC2DB0BFB5C8772D6D6

87C77417910B93D284AB9BF3CA563DB7E833BABF36739F64615ECCCD7DC537F7F59EA3AA6F6DA977

7E77BB7AF2FA9AE1C7FD3927A775490ED2684394A429B88A73D4F5365EA3D93777E71E2F72C6D9D9

9A579E8F5E5CC0C8702BD0967F9F39F4446DEE31F7D9AAD8BBB2AA0FC29964BDF6E70724B2DF7FD9

E4E5EF6C6D29219674D7EFDAC2EFC423D678F6CC93234506775D46664E5B70F8D3A59288FAAECA70

358CB041C231D352A93E670982B8E7ED2539D73A5BED62339A3FEB2906BB789F7ABCD55D1F73FBBB

58AAE439CD274BFAD6F9789C799BA2F9FAA6D625CFB8DE154D76571DA7D85FB815D87160756E9ECA

CBF7C7B71C110DFB74DD3EAFF4588FAFC2E5B8DA29C26B7BC3CE3DF75D3AE27CEA735E1C356FB7E6

EE5B47966C7E6FD2ACD7AE56A923BECED236DDB348EDC39A6FE55D8BFDBFDA377CF338B62C59533F

A32FF1E7BEFBA31DEE01531E0CFC1C685E4BF07B627143E439B3F1E7C9B6B37695E1BB8FA82F7E41

F851B0ECFD8FF93F0AF6AE63E5677D3B92F2E0B7CBEFF93FD2B6B7A57F5B525BE4D93BA7768A5054

8C5ED4F9F604F7078F090A7B2346E69F4D49FCBDE8B47FF289595F8F863257CC5A339F6B463EB1F1

66A1F2954D3D0BD2686F9F0BBD965FA5A1357DD6F1BA455E8FA417CCD588EE08BAB43CBDB5D4EAE2

E665959F7A8E3E5DA1D411123E6873F442C3CDB38BC178CFC8C2388D74BD5B6B7521EE5E9258EFDC

45510BCBD79CD076D9B869C9C87BAAC2D99CF7A68F8D281F2E85DDF615135E5FAC33A542985CD9A2

6B27B4419B4678129716665DE1EAF8CD76D6BDAFAB07A768D917AD6C3C71F5D28E21FF40A5BD23ED

0782BFCE38D077F4F4C5291D41990386B2124EF61B955F0F97F85905E0A85797ED08950858E61279

A6EAE305ADD3870E7F7D4AB9735941A2F1ACB75323B7E2DAFE0F2D87EE3F0C0F7F7A7FDAA359BFFA

9DF54DBEAA3C26BE622FEBAC989618DB9CBAFCD38D6ABDA0D3B6472FD50E7397755EAB3B84DFDB9A

692C7C54137B55583C13B775EBD3AD36D75E5FE86BFD6A1037EDDE75A7B69725A6E589510B46D7E6

D75F491CA9D7997133CD857C5FBE33B66A6165E57EA7504DC3D4455851D9A63E25AC4FC8C6355BD6

640CAF7B9CC5B8764C55B2965C6944BBB8FA4C367343EA7AEE699DD82FEACB2A7F99D4EC5B93B999

D4FF298CD2197EDC44F9FB179112E5B6B537E51B0F929327E184CA340FE6445DD77AFDFA12E14C0B

EBAE56D7C6A5B691426FC4BFCC589532CF97AA23EC9FC608D8363F3169F266AC4B5FBED5F57077F7

0F4209ED6A39751BEEE52AF99B16AE70EF6F753CBC98F959F9454301E956DBDD8EC5FBCF5E9BA614

22BD8044C77759BDD8926433C76A1F71D696EA131BC283E4E7E10BA5B4444F9F0A9C71FAEE8BD532

D70FEE08F24E2920E9B6EB5C7259B6B8432CA37FE0DDF9F637A71E3ACF89E8727B32FFE8C2BCC2DD

6FBCEF7755AC5FD8F53BABED9398EC15864CF585CC7A5FB19EC7D6B8B50F2E38BCBDE6F4A6FACA24

53759F831E1C79B5EE6B76FA9D1B7FA7A6689EAA4CFBBDEED5CCF35E975E6E34382F19B0E2D4EDEF

0BAE2A1D127B65BE286B8DCE63F1C89F0E0AAB8E04A796AFC0B6EF4DBDFEE64E5EB1BD3CC9E8C183

8C3409A50EAD9D75B716D7BF6DB00B7FE335FB5050DABC0DE65F1FBBCAC8FC98B26B831469FBAF53

BD6FB2663A8BD02D639F5E8C4E08B0AD79C4B966A27E33CE2BF1C9DA97E98BBBF7E0BE0AFD78B5A1

62CEA6170E2F8F76B7C719615D1C5CBAEE102F637476CF54BD6913BD73CF09CD0777FA2B121E5EDD

42A55EECE83DD97B71E3BED15DABAE5EFB3CCD2CE0D8FCC896BA8583B7D7BE9DE6D4783128FAECB1

D64CE2EA3203099BD48C6B33074853454EAF7A0866EA47E97B24974D217E8FB926F77B87FF8DC16B

3529670EE75AFBADCC3EBAEF454653C0364CACA5A746CC7191F5DFE3EFDCAD2A88F9FEE5677377E1

C79217CB16A82D75D36CDD971CFB33297F5BF06AC993E28BDAADF5DDA2B0BF032476ACBA9066BE47

2F43C45CD3A27DCF9AB6E97101C7882DEA55CE33C8B53A129A87B31EDEAD51C9B96B9B2FEAFC4231

EBD7A92BB93E8F5BEA5C5E33BDDF9D6CF34B3AC3D00CD4CDFD486A55AA663D183A189333774906E7

4A6C61F6B390B4CF43215377D332124D0EFA042B07ED2269196FD85C1AF954B1CB6C9190DB97BC3B

8A6D279F5C39634266CD3CAA3E45E492A6DC3CC2BC6B3558E1A7473C5AB28B0BBB5E559BB6DE30EC

F3303D90F3303FE247FFF7AEE0DE32F94A6C6C76FCBCDB8B567E5E357F4F9CC1F24F4AB1970E87D6

5E68C89652FBDD41373B965EF6E1EE74DC8B87835C97D55D57F7C93E1759607D594EFD7E8EFB70E9

ACECDA2EF967064CDAC8ECCDFA6D416BE6991D7AB2FAF0F5CF999531274DF0295BABF79089478CA7

2D5FF325A2F7785DE4AB03B9690757ACBCF13BB48371CD534971DF46D29C87EF62F52F5D1BC6DBF9

A83814F468E0B61E2C3FBB614FCBE65A51D250C2C08299A7DE7A9A3F7DD77CB07D60FBAD0F3AB936

014A8E52C2AB6F9C59FDBBCA3D7ACEE473AB886D8FB63BBFDB7BD14FF54D76E7C59485C95F07ADF3

D22E936A0A436DE94359ED34B2CAD1E9E26AF6267377EC21851F2D9EF1CE9C13382B27D9A942DA48

BBABF25990AAEC2D852B413E6D4CF1DC5D4BB61C4C76597FC3E57043B656EEA919663773039447E3

AFBDC6EDD9E57D29AB76DB92EA19D4991E3219AE66D30D7FAA19366AB574B39EE6052EBBD2BB5156

A9327CC123FB7B8F1B56484FDAFE5022CAC7DDAA5DB6E49DAC85A75ED38DE6E460D93B7B9D4FF5E7

EE1F1293C73AB464C7B43D0FB7F0B6884F0B7598FFBCF1D65B6D9651FE3CAAD2DB9CF0E083AAD8DB

96B18FE788EE13710E391A267C8771C2B9A0D4F7C3264789E7E107DDA5FA700AEA9E893B583A651D

AFA6CC253E377DED2F8AA93E45CDD8BE6BDB0C8FC180E11F932A0B557EDA494C9A62FB8E3A7BE335

BD26EEE302AAADC4B0F5C849CDE1554A4B622B429C99A76C5A6E77CEB9F8EC73C394AEBB46E16F54

3F504C22763315EB13B6B798CF6F796871CFA623436C8375AA4BE3FE75DFE5FAB7A88C4ABFCAE12E

395AF57CAD72E2DCA2F59F334EDE9832B8532671C3AB7AABD9B7764C7AF381793833F1D459130772

5152225DC726D2CDC86EB82D5F24EA8DAB8BFA943B2F926D26851F12FE2472E07D48E03BDBC3F8BE

37DBA2D63D3EBE254568C072CA86F3078AE3BC0FA914271D5B784DC7C2E5528AC5A2580B216AA884

5CF9484DAECAC9399F5C543F953CBCB74E56477E5EB87847833BF60B755FDF3D9705CF6DBB96E87C

C2ACB6BC9E6FBC98F5BEF05CA93BA676556BEC66D5DB8B138974ECFC5FFBA6C9BCFC71FE0967A7B5

C782D52BC3B5CC12CD473AEED33674EEFE7E539975E6DB5DBFD46AAB1B566F3C64EA383D5699B287

E36472D4A364F7D5CA5733D70F257CA0DAF9BBEF2FCD8BDD983AAF1B9B7E2DBE4A8892FB4E77DB30

E381CAC6F44B6FF2A29EF70EF74444179B383B0DEFEE2DC6FCBAAE6D9C7AFCD396CB7BDA147E6C88

F0B96DBB5FE79CF7F296B34E11C716C6BF617B77F86F5A9A449822CAACA2886EDAF055C1BFE3915D

F4EE0BCEDF427746CD59AC533F6CA67E68BAD2E14ECCFD979DBF1E1E6D775FFF587495F7C2A4CEDF

FD9A036775163CD87DFCA13F3E629E194B1DDB1E5D333FD0B581FDB40E0B021CF66372C40B892E9F

11A721E2AB0739DFCB9AA257149EF2BB69463B5C1EF5F2DAE88834F33CB3B1E96AE6462DDAFD2B9C

D0D3C9EB1F1D8B923A78626EA7CD8B54A91EDCE65EE353DD8B89ACA3EBDD8EA63D0CD576709ABBF3

F6C2CAA68EF27483D9DE6F83CCCF5A3115B72FDD26E1B374E44ECABC07D826E94DBEB904F5DDC6EA

997D6939E70B022F1C9BE37032865C92FB3079F26E535357E7EB9CE97D89E72206DCEC6E0CCFEEB2

BEB3FE2B63E7AA8B8BFB12E67D2C4B53BF3EAF21D5E309A9F85DA6A8EAECA5092BEB6C2EB1B21E6C

3FB172165567F39147234146F2EB67AF8A28BDF970FFCDD7510D333EDCBFBCB5AC4D66D69E80BA9C

4D23472DA4CEBC99B5B9D131B9F85BA78FF270D9BEC987772EB2C269062DC6545A9DF48C9EA49BB5

94D0A4B2429D70A84E7D60499F4983EF773D3FBAE5874DA3C925332DDB2DA54C9D355AB71FFC6573

3C7578FB8F9B370BBD49753B8AD69F505BEFAE1BA875E1AD46C17527BBE74B56EE8EB13C2CD9F345

8A6E10FA2ECB4BC2524BA9C7A09CDE467F37AF3078357D63CB4B1271D369C7DAA4A7A7714B71AE3D

350AF2F3159675E98C3C3ABCBF5AB604A77D6BD3C6DBE735A52D9FD5AC9B3534F9F099D53AD5BB6A

0D067D0AD383FC5A226FA7374C6B38BCBA745B4664B17A1331EF7EF3A057FF95DBE2EB8ABC759470

8FACB6DDEDF7AA284F5E96333C6CEB98AEEBF7B30C3728747F207524CCC3B7A4FDDB2DFA058B9025

2B231323F2B6BBE00C5C5C17D44F5E654C3961422EEF9C25FFEE8877CBF9084B1F89E9F599DEADCF

22D63C1C305B7FED39E7585FCF94C3B5CEFB5467D4BE3DB9C17432ABF66CFA938B07971E299FEAF6

959518EFEE7536A5BA0EB7E8D0C768BA9647868A58C566FF538DF4DD9BC51FDFEB977525A64FF768

BF33AFE7FA6EEBF37DF448AB7B9175E722EBD77EEA77A64D51FD847FFC66785824F3C2E85ABB3D1F

990F0F1A6FE80AFDED672C9AB67177E9A991D973CB86BBA6453D6B3E7DE39DF8E7758A07CFA61F77

553DE0B46EFDD10DF782AF4FD676FAAAF6423759D6F63ABD96335A10929FF45D25EB4BD9A45F5987

BE1D2D298FD3595B7981324C9FA6564D58F5E9CC80CDDBAD959B59981853CFF494FB6D36F479C375

EBD64815FADA18DA475DCE29AE4DEB5309C5D8A811773DA81EDA9A3C5FFFF1212FFDD0C19D03F519

CE8A5E0E3BE893562DABD110D275881B7A2632FDAE66F488E7CCABC59AA49A56A3174998EC1D43E4

B86BADAA1C1CB9FC9861DC732173C2BEC4573BE5E3030C73DA596F0BAEBA49BF69EEDFA157A4D8FA

2423EFDE9761C50732AB07A5B639A82C3AFBAEF03A51626FC925FFA70D4FBF0B9956AB5E52DBA579

B1223D694FFE994C93F9D7AE9D0811CA89280D9E5D187A6FCDA68CFE47A74FAE0B965C51BD674EDC

279FA72EEFD2AE58AF2D2A0B7B7C6D738AAC597CEA772F97454A493B55BEC9FF5850737E91998A47

C5EBDC3B8A972875AC2B21D39B89072777199F4B4B3E7C9A4ADEB148C2A364768713FB7E516F0B71

50E99576D5637A73F4198FCDED392543D4330FE76ADBDFC967B2B001E1B6A14DA7E6A926B8B506D6

6ED16EB658BD49D2C363F327CD6E8986953197F5EC6AE61FC31DC37046270925BAC8E3CF517F612A

AF27D82F61F86F7EA4187A3F39C0FAA8D9EB4DA76B9F3ABC0CB37C35E9FCEFC2A3A92E979D987BF7

14DE6447CB3D79403666286C912D5BBBAC59647F8F7F85D1769DCA2872A7CB97AE58A515B242D95B

0766EA6D10BF2A72C646A56186B0FCC15357F3E474F372B91D1411C7C25F37BF98DFD4080B4E2B89

7AB9BA26B327B8686DDD43EFC391F33728F69EA96A75FD3823E7BBC50CA5E305862BAE6CB93F94B7

CBFEEA957A7CBEF3B61D8A05FDB9BF120A16D47CCB3B46BD47FF39E01B485DB4F1BA326DE76F919D

76576965F79E30D4E4B33E8856630F473D1EF6FEF9687F95ABEC578379C72F04FFD8BCEECAEA49AE

9F569D8BD8B2E24456FAB9BBB3B3484F0A13A40B0C6C4CE54E54BCB26CDB7F7BDBACF44F6A01BF7F

9D2EB572AE4823A5FACF6EC655B84B349418AFA9D2B2C687473DDC519B3EA355E98BAD917387F18A

0D1F2446679E998729D917A7FE7689967A2BA6A57B5FFE740601734FC121F2E9B06EB799FEC59F97

03326F282E9B44FBBDFAF9D2BA358BE52FE2C5F44316EF3BA923F2E481E93E93D480D07E4CB83175

F8ED1CDCE2BDC75B86CF1CDF796CAEE987CA487C9AA6748ABF72A6C7A75E8AAB7667F0E77AA7FDF3

355857CDFCE42CF3884AD5474F1E5764446D5BBDF72A35394BC23656BCE5E62DEC5509516DABF717

7472CFE5D9CF10EA616F351F66F4B43686A80FF710EE44A8E695E42A4DD992EE58350DBF7DD04FD3

E1B253D1EBEF7B329BCB735AA7FB5AA9471CB83ED7E48D177EEBC314E6C8ABF86FE9563D17F4EC63

FDA6FAAD518EFEF48AEB7853F8964463B7F4EF9CE21B4A31FD27966F789C1C6734622EA7FB5D2A6A

4E5E52EB1265B317C70F620E24BACCE95AE99A70F2F85ACB3DDAEA4A1E424703E9B24D416B0A0DCD

1302D9876DF2956EFD6A22C8EB294E3B7BF2FBCE37BABDDAFBACCDB72D493272C87CD0A942FE3E5C

991166507FDCA232EED989C92EAE833E564375F1A63B47527D555257AAE66EFECDD9A17CC4C02F46

F4557F122D215A7FFDBCEF75DDFE5616326B970CBD72F0FFD97E2EE8F9C6D68691A1D8D6BACCF40C

99BA90E946931CEE3A86E799DC6CB7656A5FBCC4CDB40F0B25DF91787A7C5BB3EA1EDDF86EC9A04A

A37D76F78E47A488EC2B2178BF6E4EDA10347A8575A627347B8DDEE6C3D92A9269BB85D2DB235662

A5376F779FA1BF604A89C7DC962BFDCCFC54F7273377C429ED2E6FDB5D11CC997FDAE29CFDF7D4B8

1B0535CCC8A7B74F719FA767F7E444450BAFF990F5A4BBCDFC5BFAF9A251E3AAE23059118AF9E72D

44DFD46D3863D94DD3B55EECB2CCD44DA8FB2D34A2D44CDC148E5DF194183D521DB641F4FAC359D8

F0BC7B12FB9BD7895C33AF2D8FDBF3C94336E156C471DCF4058432D7E0D1F4B81D4F9B650E3BCA49

6F58BABDDE7930AB35AF58C5BD48E36DEAFC327AB7D763613B29F907E455ABF71D89DB4D52CF2A9E

92B4FEFE8C6ACC2D955417F9F3DC9234A973175D9293CE28AA1FC6286E9BD98FEBFCFD7ED1CE8DCF

3B3F68981CB92C1363FAF8C5CB058F3956F904B7E4A881F28EDA0BD8218D4CE20B8B875DD55D7112

EFEBD5DA6C5CC3F0EF435E7FCA7294E51A3C71D3FBA6D8C998373A2536F983D80C4B8E4E8DF516F3

6F9B4FCD12C3AB8CDCDFF444D5FAFD76C564CBC972471DBD8E0A4BDA5EF9389CE7BF66B5F0691AE3

B8DCF9F6F79479A132769CE5A39ECED239A1A1FE9D37D807EAE27DBA2BD53F98D0376609DF685B72

28B7667581B2CEE2A73F9CDE39ACADFDFD6D5EABF48F19614FB296756B0E6912AE7EBAA0AC273DE4

70242CFC624243D58C9192F7E2D68E014DC6D61FAFCFFC7028EBA6E89DD103B93112B759817A89BD

DA537E6AD8A8633286B81B8F4B3A2D6A25BD2E4AB962BF05EB3E3FE673EF0EC9B37ABEEF5F547449

E26C1E9BB4E76FD9547167DD91ADDB1A7F84EA54B73E2C3D6F942C6C74C24798B5FF43BA2CC36CA3

B35DE862D2B2FB11F5A1A7EAE796A9B8ED2BB07D92DB6D5C547EFB28B698A491FA2E49547D9D538F

A7F0CD3D292D3F2C064F76DEFD5A17D8D96A7DD72261D6486D4D632FD541164F6876BCFAEAED3E66

EAEC2DB60D69DBF777B26E746255A9D1BE39760E1967339EC83E316C29DEF22E4AECACE10F1DA284

D7FBA0B96E473884C7731D7282A35FAF8B364B1DAEF9DA20E17BE892B93766B9D6D96DFE8D5B0258

2F699985D555D77EB97D7E289F61DFB460D9AC64E759257B495517317D6D97ECCA5999DB6A037A56

CE137AE8470A3DFFFAB5439942F87679EFEB014CF15B3946FB3FFF9AB4E665FD592FED6583E5D9C9

8E53F7B04F386DD52C79D937EE3D572A1EE87BE3F8EF59C81F7E0B9FC30DC2FDFDB98BC62CB1D471

023B6BEB456A5A764B576BABE9EB1AE81AA969ADDE4AF7A7D179DC7C221DE1166A8FCB69A0ABA7AB

3F9613E1F8390DFFE773FE1B3D7543BC4368C134B7AD185D6FB7106F8CAEC72EFF905D7E3C4A0BC6

E86EF3A7EB867A0687F804F88F635C415AB0A7AF1BCC889E05FAD230BAC8AB9F74699E61E03FF202

28DDE000E42D36BA9EDEAE5EC16E7E9EAEDE1EC17F388CAE3B2D20380434C823DBDD8391C6DDFC7C

DC41830134E41FAF6E5E3D5B434036F7003F3F4F7FDA7FCF633A12E8334FB809CF41F1A9259AC04F

E73F8F242CF04E49098174FE73527C2A26FAE7B9284981F2FCE797A6A069B809CF5DF16984D0DF9F

EFE27FD4D1679EF8E5F9CF39F1E931CC78FD27525DF4192A3ECF7F8E8A4FD5049EEBC20ABE7F07FD

98A3B6C04D786E8B4FF9CF6D4DB41FBFFF7613CAF39F03E3D3F302F69FF497F2CB3102EFAA1578CE

8D4F27BE2B74E2735F8E13CA37C98DA715D8BF97E73F8DB4764279FEFB3DF9F481DEF8F2139F62DA

34A13CFFB93C3E95FE37FABBA3E5F9EF20E2BF2F944FF1FFF07C209FEE40CFF9F6FFA7F7EEFE53FB

BB2694E7BF7F944FCD261498881F065A7E0CE663EF19FEFBF84D2C7F604279FEFB4C959DFF6EEF89

E5932794E7BFF754CBF9EFF927F2C7D0B1139AF0BC1EFFFDC7FCF71C0B4D28CFD7EBDC84F6F9EFAD

CA5A3ECE0CFFD23E9F5E9A683FF4B94BFE8B3A07FE4DF99B13CB8FBD6F0B7D0FD28497014E7C37E0

5DB42E7E79FE7B60E343D0F71C19FEC7E51FFC25A60996AFC2FCC7F16F9CEE021F17747E28F837E5

FF0F67F99C05 -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/htm/ma001393.htm: -------------------------------------------------------------------------------- 1 | Not Yet Supported -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/htm/mc002306.htm: -------------------------------------------------------------------------------- 1 | Not Yet Supported -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbag.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbag.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbags.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbags.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbcl.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbcl.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbcls.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbcls.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbns.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbns.dat -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbsec.htm: -------------------------------------------------------------------------------- 1 |

2 |

To Manually update your local PunkBuster Security Files, 3 | choose "Save As" from your Web Browser's "File" Menu and explore to 4 | find your game folder and the subfolder called "pb", then save this file 5 | in that location (/pb/pbsec.htm or /pb/pbsecsv.htm). 6 | The next time you start your game, your PunkBuster will have the latest 7 | security information necessary to accept auto-updates from PB Servers. 8 |

9 |

S 3 MASTER5.EVENBALANCE.COM 16567 0 10 |

S 4 MASTER6.EVENBALANCE.COM 16567 0 11 |

S 5 MASTER7.EVENBALANCE.COM 16567 0 12 |

S 2 BF21.EVENBALANCE.COM 16567 0 13 |

M BF22.EVENBALANCE.COM 16567 14 |

F A K 1393 7B60 48AB9BE59074C258CE2C758DD38F5F2F E14C9AF0134D6C493FEFACD9F684C0B5 15 |

F C K 2306 76CF0 EB739E71D5C5758D9749199CA2DA3BDF 22C8077205319247CD9DE1FE07AAC833 16 |

F S K 1800 BF0F0 8F9302F5BD776347AA15D93B658CFA9D D366F0E8009DF62E4433226F73C7F739 17 |

F A L 1393 112BC F3D5B051895295547B25B730A5532DAB 8F5F431A43F8D3C45D6AF9694D9BDBFC 18 |

F C L 2306 DF764 3FA4720F15D5323180E7EC6740D5DD00 640CFF763A0B491BD430082FEBEBC098 19 |

F S L 1800 144244 60CE7BF03014B2109BF18094B7807937 77040B56295447BC5CBF300034F460ED 20 |

F A M 1393 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 21 |

F C M 2306 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 22 |

F S M 1800 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 23 |

F A W 1393 10000 2EB9DF6C1CA87B2DAD817BF3B66939D4 839BDB6CDCB638290C1C645F30A5FFB6 24 |

F C W 2306 E9F54 019D54A9CD7385B9F32EAE49F87B3493 ED8C3FE257A1EFAC3705394C9A441195 25 |

F S W 1800 66000 B5DA0C51C5806C17696B1C5E11B11855 856849563D318794E1C67FA1B4A027F3 26 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbsecsv.htm: -------------------------------------------------------------------------------- 1 |

2 |

To Manually update your local PunkBuster Security Files, 3 | choose "Save As" from your Web Browser's "File" Menu and explore to 4 | find your game folder and the subfolder called "pb", then save this file 5 | in that location (/pb/pbsec.htm or /pb/pbsecsv.htm). 6 | The next time you start your game, your PunkBuster will have the latest 7 | security information necessary to accept auto-updates from PB Servers. 8 |

9 |

S 3 MASTER5.EVENBALANCE.COM 16567 0 10 |

S 4 MASTER6.EVENBALANCE.COM 16567 0 11 |

S 5 MASTER7.EVENBALANCE.COM 16567 0 12 |

S 2 BF21.EVENBALANCE.COM 16567 0 13 |

M BF22.EVENBALANCE.COM 16567 14 |

F A K 1393 7B60 48AB9BE59074C258CE2C758DD38F5F2F E14C9AF0134D6C493FEFACD9F684C0B5 15 |

F C K 2306 76CF0 EB739E71D5C5758D9749199CA2DA3BDF 22C8077205319247CD9DE1FE07AAC833 16 |

F S K 1800 BF0F0 8F9302F5BD776347AA15D93B658CFA9D D366F0E8009DF62E4433226F73C7F739 17 |

F A L 1393 112BC F3D5B051895295547B25B730A5532DAB 8F5F431A43F8D3C45D6AF9694D9BDBFC 18 |

F C L 2306 DF764 3FA4720F15D5323180E7EC6740D5DD00 640CFF763A0B491BD430082FEBEBC098 19 |

F S L 1800 144244 60CE7BF03014B2109BF18094B7807937 77040B56295447BC5CBF300034F460ED 20 |

F A M 1393 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 21 |

F C M 2306 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 22 |

F S M 1800 11 74DF4244ADCD334C1C7A77D0142D4F2D 9ACCA9C508F42331DA7077343A9C92A4 23 |

F A W 1393 10000 2EB9DF6C1CA87B2DAD817BF3B66939D4 839BDB6CDCB638290C1C645F30A5FFB6 24 |

F C W 2306 E9F54 019D54A9CD7385B9F32EAE49F87B3493 ED8C3FE257A1EFAC3705394C9A441195 25 |

F S W 1800 66000 B5DA0C51C5806C17696B1C5E11B11855 856849563D318794E1C67FA1B4A027F3 26 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbsv.cfg: -------------------------------------------------------------------------------- 1 | ; ------------------------------ 2 | ; General Settings 3 | ; ------------------------------ 4 | pb_sv_MsgPrefix "PunkBuster Server" 5 | pb_sv_MaxDlRate 4 //[KB/sec requested per file (default=4)] 6 | pb_sv_MaxConDls 6 //[Concurrent downloads (default=1)] 7 | pb_sv_MaxSendRate 8 // [speed at which PB Server sends pbcl and pbag updates in KBps (default=8)] 8 | pb_sv_CQC 1 //[0=No, 1=Yes (default=1)] 9 | pb_sv_Sleep 60 //[# of Milliseconds (default=60)] 10 | pb_sv_PowerMin 10 //[Power Points] 11 | pb_sv_PowerDef 1 //[Power Points] 12 | pb_sv_PowerKickLen 5 //[Minutes (default=5)] 13 | pb_sv_DupNameGrace 0 //[Seconds] 14 | pb_sv_ExtChar 0 //[0=No, 1=Yes (default=0)] 15 | pb_sv_GuidRelax 7 //[1=UNKN, 2=WRONGIP, 4=DUP (add desired values)] 16 | pb_sv_RconReload 0 //[0=No, 1=Yes (default=0)] 17 | pb_sv_LogSync 0 //[0=No, 1=Yes (default=0)] 18 | pb_sv_AutoUpdBan 1 //[0=No, 1=Yes (default=0)] 19 | 20 | 21 | ; ------------------------------ 22 | ; Other Settings 23 | ; ------------------------------ 24 | pb_sv_Restrictions 1 //[Kick for Restrictions 0=No, 1=Yes (default=1) 2=key macro Restrictions] 25 | pb_sv_FileWhitelist "" //[Folder Filename Filename ... Filename] 26 | pb_sv_EmptyName 0 //[0=No, 1=Yes (default=0)] 27 | pb_sv_LogFloor 1 //[Low log filename serial #] 28 | pb_sv_MinName 0 //[Min Characters in Player name (default=0)] 29 | pb_sv_MaxName 0 //[Max Characters in Player name (default=0)] 30 | pb_sv_LanMask "" //[IP Address Mask for LAN Players (default=)] 31 | pb_sv_Lan 0 //[0=No, 1=Yes (default=0)] 32 | pb_sv_ChangePeriod 600 // For pb_sv_ChangeMax 33 | pb_sv_ChangeMax 5 // [Max name changes allowed in pb_sv_ChangePeriod] ex 5 changes in 600 seconds 34 | 35 | 36 | ; ------------------------------ 37 | ; Kick Settings 38 | ; ------------------------------ 39 | pb_sv_KickLen 1 //[Minutes (default=2)] 40 | pb_sv_ScoreKick 0 //[Min score (negative) ] 41 | pb_sv_UpdateGrace 2400 //[Seconds to wait before Update Failure kick] 42 | pb_sv_NoGuidGrace 10 //[Seconds to wait before No GUID kick] 43 | 44 | 45 | ; ------------------------------ 46 | ; PBSS Settings 47 | ; ------------------------------ 48 | pb_sv_AutoSs 0 //[0=No, 1=Yes (default=0)] 49 | pb_sv_SsFloor 1 //[Low screenshot filename serial #] 50 | pb_sv_SsCeiling 500 //[High screenshot filename serial #] 51 | pb_sv_AutoSsFrom 200 //[Min # of seconds to wait before requesting next ss] 52 | pb_sv_AutoSsTo 1200 //[Max # of seconds to wait before requesting next ss] 53 | pb_sv_SsCmd "" //[Filename of system command to run after screenshots] 54 | pb_sv_SsWidth 1024 //[Requested pixel width of remote screenshots] 55 | pb_sv_SsHeight 768 //[Requested pixel height of remote screenshots] 56 | pb_sv_SsXpct 50 //[Percentage across screen for remote screenshots] 57 | pb_sv_SsYpct 50 //[Percentage down screen for remote screenshots] 58 | pb_sv_SsSrate 2 //[Sample Rate for remote screenshots] 59 | pb_sv_SsDelay 3 //[Maximum delay client waits before capturing screenshot] 60 | pb_sv_SsPath "" //[Path where remote screenshots are saved] 61 | pb_sv_ssLogging 3 //[1=Reg. Log, 2=SS Log, 3=Both, 0=Neither] 62 | pb_sv_ssTimeout 300 //[Seconds] 63 | 64 | 65 | ; ------------------------------ 66 | ; Web Tool Settings 67 | ; ------------------------------ 68 | pb_sv_HttpPort 0 //[Port #] 69 | pb_sv_HttpAddr "" //[External IP Address] 70 | pb_sv_HttpKey "" //[Password] 71 | pb_sv_HttpRefresh 30 //[Seconds] 72 | pb_sv_HttpMaps "" //[Map list (separate by spaces)] 73 | pb_sv_HttpMapsPath "" //[Path where maps are loaded from in WebTool] 74 | pb_sv_HttpColText1 "FFFFFF" //[Text Color #1 in WebTool (default=FFFFFF)] 75 | pb_sv_HttpColText2 "0000FF" //[Text Color #2 in WebTool (default=0000FF)] 76 | pb_sv_HttpColBack1 "000000" //[Background Color #1 in WebTool (default=000000)] 77 | pb_sv_HttpColBack2 "808080" //[Background Color #2 in WebTool (default=808080)] 78 | pb_sv_HttpColLine1 "FF0000" //[Line Color #1 in WebTool (default=FF0000)] 79 | pb_sv_HttpColLine2 "0000FF" //[Line Color #2 in WebTool (default=0000FF)] 80 | pb_sv_HttpColMsg "FF0000" //[Message Color in WebTool (default=FF0000)] 81 | pb_sv_HttpShowGuid 1 //[0=No, 1=Yes (default=0)] 82 | 83 | 84 | ; ------------------------------ 85 | ; MD5 Tool Settings 86 | ; ------------------------------ 87 | pb_sv_md5toolfreq 100 // [Reducing this will increase the scan frequency that can cause lag and also increases the chances being kicked for Ignoring MD5 Tool Queries.] 88 | 89 | 90 | ; ------------------------------ 91 | ; Alias Settings (Only for supported games) 92 | ; ------------------------------ 93 | pb_sv_AliasFn "pbalias.dat" //[Filename (default="pbalias.dat")] 94 | pb_sv_AliasAutoLoad 0 //[0=No, 1=Yes (default=0)] 95 | pb_sv_AliasMax 10 //[Max # of Aliases to track for each PB GUID] 96 | pb_sv_AliasMaxEnforce 0 //[0=No, 1=Yes (default=0)] 97 | 98 | 99 | ; ------------------------------ 100 | ; PB Tasks (pb_sv_task [start delay (seconds)] [repeat (seconds)] [command]) 101 | ; ------------------------------ 102 | pb_sv_task 0 7200 pb_sv_ver // Keep-Alive for PBBans Hub (Do not remove. Will cause servers to appear as inactive when empty for long periods of time) 103 | pb_sv_task 0 86400 pb_sv_update // Check for PB updates daily (More dependable than UCON system) 104 | pb_sv_task 300 691200 pb_sv_md5toolempty // re-update MD5Tool list 105 | pb_sv_task 340 691200 pb_sv_load pbsvuser_mods_md5.cfg 106 | 107 | 108 | ; ------------------------------ 109 | ; Badname List (pb_sv_badname [grace_period_secs] [disallowed text]) 110 | ; ------------------------------ 111 | pb_sv_badnameempty 112 | 113 | 114 | ; ------------------------------ 115 | ; PB UCON Settings / Lists 116 | ; ------------------------------ 117 | pb_sv_usessionlimit 10 118 | pb_sv_ucontimeout 300 119 | pb_sv_uconempty 120 | pb_sv_uconadd 1 66.55.152.232 "pbbhub3-1" "pbbanshub" 121 | pb_sv_uconadd 1 66.55.152.233 "pbbhub3-2" "pbbanshub" 122 | pb_sv_uconadd 1 66.55.152.234 "pbbhub3-3" "pbbanshub" 123 | pb_sv_ProtectTag 1 GGC 124 | pb_sv_uconadd 1 31.214.160.253 "ggc" "bf2" 125 | 126 | 127 | ; ------------------------------ 128 | ; PB UCON Ignore List (Prevents UCON users from sending various commands) 129 | ; ------------------------------ 130 | pb_sv_uconignoreempty 131 | pb_sv_uconignore pb_sv_uconignoreempty 132 | pb_sv_uconignore quit 133 | pb_sv_uconignore pb_sv_md5toolfreq 134 | pb_sv_uconignore pb_sv_GuidRelax 135 | 136 | 137 | ; ------------------------------ 138 | ; Misc 139 | ; ------------------------------ 140 | pb_sv_writecfg pbucon.use // Enables UCON in case the server removes the file for any reason. 141 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbsv.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nihlen/bf2-docker/ab1230922eb973f59aecacc8ac59604b67921456/images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbsv.so -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/pb_amd-64/pbsvuser_mods_md5.cfg: -------------------------------------------------------------------------------- 1 | pb_sv_md5tool a "" v mods\bf2\Booster_client.zip SZ169667159 AT0 LEN2048 99D4DA6EA34CA44A6F77B746F61AF9BC 2 | pb_sv_md5tool a "" v mods\bf2\Common_client.zip SZ149828360 AT0 LEN2048 D7A9B566A56DE4DAC0629E9A2BD01E93 3 | pb_sv_md5tool a "" v mods\bf2\Fonts_client.zip SZ12284200 AT0 LEN2048 82DF5D46C2965616E23899185CBDDB12 4 | pb_sv_md5tool a "" v mods\bf2\Menu_client.zip SZ42196891 AT0 LEN2048 158F9C949BF01591C5079239FD871725 5 | pb_sv_md5tool a "" v mods\bf2\Objects_client.zip SZ641042098 AT0 LEN2048 691C0339D9BFDD885627F2A9EFECC058 6 | pb_sv_md5tool a "" v mods\bf2\Shaders_client.zip SZ224632 AT0 LEN2048 6D61C1FC1531A93B2E10C1632B23C350 7 | pb_sv_md5tool a "" v mods\bf2\ClientArchives.con SZ306 AT0 LEN256 3C80942192F21D95FE6FBA3907955DAB 8 | pb_sv_md5tool a "" v mods\bf2\GameLogicInit.con SZ13011 AT0 LEN2048 D9D74730755B6C83EBB2993B79692525 9 | pb_sv_md5tool a "" v mods\bf2\Init.con SZ301 AT0 LEN256 3872BE35FB5754D9ABB4A8D3FBF82066 10 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/bf2/rotate_demo.cfg: -------------------------------------------------------------------------------- 1 | # set the number of demo files to keep in rotation 2 | file_limit = 50 3 | 4 | # for local web server; edit this path and make it match the one in serversettings.con 5 | #target_root = /var/www/html 6 | target_root = /volume 7 | 8 | # set to 1 to enable ftp uploading 9 | use_ftp = 0 10 | 11 | # set to the target directory on the ftp server 12 | ftp_target_dir = /path/to/webroot/demos 13 | 14 | # login information for the ftp server goes here 15 | ftp_server = my.ftp.server 16 | ftp_user = my_user 17 | ftp_password = my_password 18 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | TMP='/home/bf2/tmp' 6 | 7 | INSTALLER="$TMP/bf2-linuxded-1.5.3153.0-installer.sh" 8 | INSTALLER_TGZ="$TMP/bf2-linuxded-1.5.3153.0-installer.tgz" 9 | BF2HUB_TGZ="$TMP/BF2Hub-Unranked-Linux-R3.tar.gz" 10 | MODMANAGER_ZIP="$TMP/ModManager-v2.2c.zip" 11 | 12 | # Get required packages 13 | apt -y update 14 | apt-get -y update 15 | apt-get -y install wget expect unzip 16 | 17 | # Download missing assets 18 | while IFS=" " read url filename 19 | do 20 | args=(-nc -q --show-progress --progress=bar:force:noscroll) 21 | if [ -n "$filename" ]; then 22 | args+=(-O "$filename") 23 | fi 24 | wget "${args[@]}" "$url" 25 | done < assets.txt 26 | 27 | # Verify checksums 28 | if ! sha512sum -w -c assets.sha512; then 29 | echo 'Downloaded file checksum mismatch. Exiting.'; 30 | exit 1; 31 | fi 32 | 33 | # Extract server files from the installer 34 | tar -xvf $INSTALLER_TGZ -C $TMP 35 | chmod +x $INSTALLER ./extract 36 | ./extract 37 | 38 | # Move BF2Hub files into server directory 39 | tar -xvf $BF2HUB_TGZ -C "$TMP/srv" 40 | 41 | # Move ModManager files into server directory 42 | unzip $MODMANAGER_ZIP -d "$TMP/srv" 43 | 44 | # Clean up unused folders (we have updated pb) 45 | rm -r $TMP/srv/pb_* $TMP/srv/bin/ia-32 46 | 47 | # Replace with our own BF2 server files (custom settings and scripts) 48 | cp -r "$TMP/bf2/." "$TMP/srv" 49 | 50 | # Create empty server folder to copy our files into if it's empty on the host system 51 | mkdir -p $VOLUME 52 | chmod -R 700 $VOLUME/ 53 | 54 | exit 0 55 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/build/extract: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect 2 | 3 | set timeout -1 4 | spawn "./bf2-linuxded-1.5.3153.0-installer.sh" --keep --target ./srv/ 5 | 6 | expect { 7 | eof { send_user "\nunexpected eof in extraction\n"; exit 1 } 8 | "*ress return" 9 | } 10 | 11 | send "^c" 12 | 13 | send_user "\nExtraction finished\n" -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/runtime/nginx/default: -------------------------------------------------------------------------------- 1 | # Default nginx server configuration 2 | # 3 | server { 4 | listen 80 default_server; 5 | listen [::]:80 default_server; 6 | 7 | # root /var/www/html; 8 | root /volume/www; 9 | 10 | # Add index.php to the list if you are using PHP 11 | index index.html index.htm index.nginx-debian.html; 12 | 13 | server_name _; 14 | 15 | location /demos/ { 16 | # First attempt to serve request as file, then 17 | # as directory, then fall back to displaying a 404. 18 | alias /volume/demos/uploaded/; 19 | try_files $uri $uri/ =404; 20 | autoindex on; 21 | autoindex_format json; 22 | } 23 | 24 | location ~ ([^\/]+\.bf2demo)$ { 25 | alias /volume/demos/uploaded/$1; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/runtime/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | TMP='/home/bf2/tmp' 5 | SRV='/home/bf2/srv' 6 | VOLUME='/volume' 7 | 8 | generate_pw() { 9 | echo "$(< /dev/urandom tr -dc _A-Z-a-z-0-9 | head -c10)" 10 | } 11 | 12 | replace_var() { 13 | echo "$3: $1 => $2" 14 | replaceEscaped=$(echo "$2" | sed 's/[&/\]/\\&/g') 15 | sed -i -e "s/$1/$replaceEscaped/g" $3 16 | } 17 | 18 | # Check if target volume is empty 19 | if [ "$(ls -A $SRV)" ]; then 20 | echo "$SRV is not empty. Skipping..." 21 | else 22 | # Move server files to persisted folder (-n without overwriting) 23 | echo "$SRV is empty. Moving server files..." 24 | mv -n $TMP/srv/* $SRV/ 25 | 26 | # Create volume directory for all persisted changes 27 | echo 'Moving persisted data and creating symlinks...' 28 | mkdir -m 777 -p $VOLUME 29 | mkdir -m 777 -p $VOLUME/svlogs 30 | mkdir -m 777 -p $VOLUME/svss 31 | mkdir -m 777 -p $VOLUME/demos 32 | mkdir -m 777 -p $VOLUME/demos/pending 33 | mkdir -m 777 -p $VOLUME/demos/uploaded 34 | mkdir -m 777 -p $VOLUME/www 35 | install -m 777 /dev/null $VOLUME/bf2.log 36 | install -m 777 /dev/null $VOLUME/modmanager.log 37 | install -m 777 /dev/null $VOLUME/pbalias.dat 38 | install -m 777 /dev/null $VOLUME/sv_viol.log 39 | mv -n $SRV/mods/bf2/settings $VOLUME 40 | chmod -R 777 $VOLUME/settings 41 | rm -rf $SRV/mods/bf2/demos 42 | rm -rf $SRV/pb_amd-64/svss 43 | rm -rf $SRV/pb_amd-64/svlogs 44 | ln -sf $VOLUME/settings $SRV/mods/bf2/settings 45 | ln -sf $VOLUME/demos/pending $SRV/mods/bf2/demos 46 | ln -sf $VOLUME/svss $SRV/pb_amd-64/svss 47 | ln -sf $VOLUME/svlogs $SRV/pb_amd-64/svlogs 48 | ln -sf $VOLUME/bf2.log $SRV/bf2.log 49 | ln -sf $VOLUME/modmanager.log $SRV/modmanager.log 50 | ln -sf $VOLUME/pbalias.dat $SRV/pbalias.dat 51 | ln -sf $VOLUME/sv_viol.log $SRV/pb_amd-64/sv_viol.log 52 | 53 | # Set execute permissions 54 | echo 'Setting execute permissions...' 55 | cd $SRV 56 | chmod +x ./start_bf2hub.sh ./bin/amd-64/bf2 57 | chmod -R 777 ./pb_amd-64 58 | chmod -R 777 . # temp D: 59 | 60 | # Set server settings from environment variables 61 | replace_var '{{server_name}}' "${ENV_SERVER_NAME:-"bf2-docker"}" "$SRV/mods/bf2/settings/serversettings.con" 62 | replace_var '{{max_players}}' "${ENV_MAX_PLAYERS:-"16"}" "$SRV/mods/bf2/settings/serversettings.con" 63 | replace_var '{{server_port}}' "${ENV_SERVER_PORT:-"16567"}" "$SRV/mods/bf2/settings/serversettings.con" 64 | replace_var '{{gamespy_port}}' "${ENV_GAMESPY_PORT:-"29900"}" "$SRV/mods/bf2/settings/serversettings.con" 65 | replace_var '{{demos_url}}' "${ENV_DEMOS_URL:-"http://example.com/demos/"}" "$SRV/mods/bf2/settings/serversettings.con" 66 | replace_var '{{rcon_password}}' "${ENV_RCON_PASSWORD:-"$(generate_pw)"}" "$SRV/mods/bf2/settings/modmanager.con" 67 | fi 68 | 69 | # Start nginx for demos 70 | service nginx start 71 | 72 | # Start Battlefield 2 server as the bf2 user 73 | echo "Starting Battlefield 2 server..." 74 | export TERM=xterm 75 | su -c "cd $SRV && ./start_bf2hub.sh >bf2.log" - bf2 76 | 77 | exit 0 78 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/runtime/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | WWW='/var/www/html' 6 | DEMOS="$WWW/demos" 7 | TMP='/home/bf2/tmp' 8 | 9 | # Get required packages and create our user 10 | # libncurses5 = run bf2 11 | # python = rotate_demo.py 12 | # nginx = host demos 13 | apt -y update 14 | apt-get -y update 15 | apt-get -y install libncurses5 python nginx 16 | apt-get clean 17 | rm -rf /var/lib/apt/lists/* 18 | useradd --create-home --shell /bin/bash bf2 19 | 20 | # Replace nginx settings 21 | mv "$TMP/nginx/default" '/etc/nginx/sites-available/' 22 | 23 | # Delete temp files, but not the temp server directory to move during start 24 | find $TMP/* -maxdepth 0 -type d,f -not -name 'srv' -not -name 'run.sh' -exec rm -r "{}" \; 25 | 26 | # Create empty server folder to copy our files into if it's empty on the host system 27 | mkdir -p $VOLUME 28 | chmod -R 700 $VOLUME/ 29 | 30 | # Create demos web folder 31 | mkdir -p $DEMOS 32 | chmod -R 777 $DEMOS/ 33 | 34 | # Change owner 35 | chown -R bf2:bf2 /home/bf2/ 36 | chmod -R 700 /home/bf2/ 37 | 38 | exit 0 39 | -------------------------------------------------------------------------------- /images/bf2hub-pb-mm/assets/runtime/www/index.html: -------------------------------------------------------------------------------- 1 | :) -------------------------------------------------------------------------------- /images/default/.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .vscode 3 | build.bat 4 | -------------------------------------------------------------------------------- /images/default/Dockerfile: -------------------------------------------------------------------------------- 1 | # Build stage 2 | FROM debian:bullseye-slim AS build 3 | 4 | # Add assets to image 5 | WORKDIR /home/bf2/tmp 6 | COPY ./assets/build ./ 7 | 8 | # Download and extract server files 9 | RUN bash -x ./build.sh 10 | 11 | # Runtime stage 12 | FROM debian:bullseye-slim AS runtime 13 | WORKDIR /home/bf2/tmp 14 | LABEL maintainer=nihlen 15 | 16 | # Environment variables 17 | ENV SERVER_NAME="bf2-docker" 18 | 19 | # Copy runtime assets 20 | COPY ./assets/runtime ./ 21 | 22 | # Install required packages and set permissions 23 | RUN bash -x ./setup.sh 24 | 25 | # Copy server files from the build stage 26 | COPY --from=build /home/bf2/tmp/srv ./srv 27 | 28 | # Move server files to persisted folder and start server 29 | CMD ./run.sh 30 | -------------------------------------------------------------------------------- /images/default/assets/build/assets.sha512: -------------------------------------------------------------------------------- 1 | b807684116a0f3d2590390567a5a0da8fa9b0804fb9229ae056fa4cad2d8a46cd306d39592a04167c8b757083e4d20a1d28fe04154c25dab3156ef8be9db3702 bf2-linuxded-1.5.3153.0-installer.tgz 2 | -------------------------------------------------------------------------------- /images/default/assets/build/assets.txt: -------------------------------------------------------------------------------- 1 | https://www.bf-games.net/downloads/mirror/2956 bf2-linuxded-1.5.3153.0-installer.tgz 2 | -------------------------------------------------------------------------------- /images/default/assets/build/bf2/mods/bf2/settings/serversettings.con: -------------------------------------------------------------------------------- 1 | sv.serverName "{{server_name}}" 2 | sv.password "" 3 | sv.internet 1 4 | sv.serverIP "0.0.0.0" 5 | sv.serverPort {{server_port}} 6 | sv.welcomeMessage "" 7 | sv.punkBuster 1 8 | sv.allowFreeCam 1 9 | sv.allowExternalViews 1 10 | sv.allowNoseCam 1 11 | sv.hitIndicator 1 12 | sv.maxPlayers {{max_players}} 13 | sv.numPlayersNeededToStart 2 14 | sv.notEnoughPlayersRestartDelay 15 15 | sv.startDelay 15 16 | sv.endDelay 15 17 | sv.spawnTime 15 18 | sv.manDownTime 15 19 | sv.endOfRoundDelay 15 20 | sv.ticketRatio 100 21 | sv.roundsPerMap 3 22 | sv.timeLimit 0 23 | sv.scoreLimit 0 24 | sv.soldierFriendlyFire 100 25 | sv.vehicleFriendlyFire 100 26 | sv.soldierSplashFriendlyFire 100 27 | sv.vehicleSplashFriendlyFire 100 28 | sv.tkPunishEnabled 0 29 | sv.tkNumPunishToKick 0 30 | sv.tkPunishByDefault 0 31 | sv.votingEnabled 1 32 | sv.voteTime 90 33 | sv.minPlayersForVoting 2 34 | sv.teamVoteOnly 0 35 | sv.gameSpyPort {{gamespy_port}} 36 | sv.allowNATNegotiation 0 37 | sv.interfaceIP "0.0.0.0" 38 | sv.autoRecord 0 39 | sv.demoIndexURL {{demos_url}} 40 | sv.demoDownloadURL {{demos_url}} 41 | sv.autoDemoHook "adminutils/demo/rotate_demo.py" 42 | sv.demoQuality 10 43 | sv.adminScript "modmanager" 44 | sv.timeBeforeRestartMap 5 45 | sv.autoBalanceTeam 0 46 | sv.teamRatioPercent 100 47 | sv.voipEnabled 1 48 | sv.voipQuality 5 49 | sv.voipServerRemote 0 50 | sv.voipServerRemoteIP "" 51 | sv.voipServerPort 55126 52 | sv.voipBFClientPort 55123 53 | sv.voipBFServerPort 55124 54 | sv.voipSharedPassword "" 55 | sv.useGlobalRank 0 56 | sv.useGlobalUnlocks 1 57 | sv.sponsorText "" 58 | sv.sponsorLogoURL "" 59 | sv.communityLogoURL "" 60 | sv.radioSpamInterval 6 61 | sv.radioMaxSpamFlagCount 6 62 | sv.radioBlockedDurationTime 30 63 | sv.numReservedSlots 0 64 | sv.friendlyFireWithMines 0 65 | sv.coopBotCount 32 66 | sv.coopBotDifficulty 50 67 | sv.coopBotRatio 50 68 | sv.noVehicles 0 69 | -------------------------------------------------------------------------------- /images/default/assets/build/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | TMP='/home/bf2/tmp' 6 | 7 | INSTALLER="$TMP/bf2-linuxded-1.5.3153.0-installer.sh" 8 | INSTALLER_TGZ="$TMP/bf2-linuxded-1.5.3153.0-installer.tgz" 9 | 10 | # Get required packages 11 | apt -y update 12 | apt-get -y update 13 | apt-get -y install wget expect 14 | 15 | # Download missing assets 16 | while IFS=" " read url filename 17 | do 18 | args=(-nc -q --show-progress --progress=bar:force:noscroll) 19 | if [ -n "$filename" ]; then 20 | args+=(-O "$filename") 21 | fi 22 | wget "${args[@]}" "$url" 23 | done < assets.txt 24 | 25 | # Verify checksums 26 | if ! sha512sum -w -c assets.sha512; then 27 | echo 'Downloaded file checksum mismatch. Exiting.'; 28 | exit 1; 29 | fi 30 | 31 | # Extract server files from the installer 32 | tar -xvf $INSTALLER_TGZ -C $TMP 33 | chmod +x $INSTALLER ./extract 34 | ./extract 35 | 36 | # Clean up unused folders 37 | rm -r $TMP/srv/bin/ia-32 38 | 39 | # Replace with our own BF2 server files (custom settings and scripts) 40 | cp -r "$TMP/bf2/." "$TMP/srv" 41 | 42 | # Create empty server folder to copy our files into if it's empty on the host system 43 | mkdir -p $VOLUME 44 | chmod -R 700 $VOLUME/ 45 | 46 | exit 0 47 | -------------------------------------------------------------------------------- /images/default/assets/build/extract: -------------------------------------------------------------------------------- 1 | #!/usr/bin/expect 2 | 3 | set timeout -1 4 | spawn "./bf2-linuxded-1.5.3153.0-installer.sh" --keep --target ./srv/ 5 | 6 | expect { 7 | eof { send_user "\nunexpected eof in extraction\n"; exit 1 } 8 | "*ress return" 9 | } 10 | 11 | send "^c" 12 | 13 | send_user "\nExtraction finished\n" -------------------------------------------------------------------------------- /images/default/assets/runtime/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | TMP='/home/bf2/tmp' 5 | SRV='/home/bf2/srv' 6 | VOLUME='/volume' 7 | 8 | replace_var() { 9 | echo "$3: $1 => $2" 10 | replaceEscaped=$(echo "$2" | sed 's/[&/\]/\\&/g') 11 | sed -i -e "s/$1/$replaceEscaped/g" $3 12 | } 13 | 14 | # Check if target volume is empty 15 | if [ "$(ls -A $SRV)" ]; then 16 | echo "$SRV is not empty. Skipping..." 17 | else 18 | # Move server files to persisted folder (-n without overwriting) 19 | echo "$SRV is empty. Moving server files..." 20 | mv -n $TMP/srv/* $SRV/ 21 | 22 | # Create volume directory for all persisted changes 23 | echo 'Moving persisted data and creating symlinks...' 24 | mkdir -m 777 -p $VOLUME 25 | install -m 777 /dev/null $VOLUME/bf2.log 26 | install -m 777 /dev/null $VOLUME/pbalias.dat 27 | install -m 777 /dev/null $VOLUME/sv_viol.log 28 | mv -n $SRV/mods/bf2/settings $VOLUME 29 | chmod -R 777 $VOLUME/settings 30 | ln -sf $VOLUME/settings $SRV/mods/bf2/settings 31 | ln -sf $VOLUME/bf2.log $SRV/bf2.log 32 | ln -sf $VOLUME/pbalias.dat $SRV/pbalias.dat 33 | 34 | # Set execute permissions 35 | echo 'Setting execute permissions...' 36 | cd $SRV 37 | chmod +x ./start.sh ./bin/amd-64/bf2 38 | chmod -R 777 . # temp D: 39 | 40 | # Set server settings from environment variables 41 | replace_var '{{server_name}}' "${ENV_SERVER_NAME:-"bf2-docker"}" "$SRV/mods/bf2/settings/serversettings.con" 42 | replace_var '{{max_players}}' "${ENV_MAX_PLAYERS:-"16"}" "$SRV/mods/bf2/settings/serversettings.con" 43 | replace_var '{{server_port}}' "${ENV_SERVER_PORT:-"16567"}" "$SRV/mods/bf2/settings/serversettings.con" 44 | replace_var '{{gamespy_port}}' "${ENV_GAMESPY_PORT:-"29900"}" "$SRV/mods/bf2/settings/serversettings.con" 45 | fi 46 | 47 | # Start Battlefield 2 server as the bf2 user 48 | echo "Starting Battlefield 2 server..." 49 | export TERM=xterm 50 | su -c "cd $SRV && ./start.sh >bf2.log" - bf2 51 | 52 | exit 0 53 | -------------------------------------------------------------------------------- /images/default/assets/runtime/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | VOLUME='/home/bf2/srv' 5 | TMP='/home/bf2/tmp' 6 | 7 | # Get required packages and create our user 8 | # libncurses5 = run bf2 9 | apt -y update 10 | apt-get -y update 11 | apt-get -y install libncurses5 12 | apt-get clean 13 | rm -rf /var/lib/apt/lists/* 14 | useradd --create-home --shell /bin/bash bf2 15 | 16 | # Delete temp files, but not the temp server directory to move during start 17 | find $TMP/* -maxdepth 0 -type d,f -not -name 'srv' -not -name 'run.sh' -exec rm -r "{}" \; 18 | 19 | # Create empty server folder to copy our files into if it's empty on the host system 20 | mkdir -p $VOLUME 21 | chmod -R 700 $VOLUME/ 22 | 23 | # Change owner 24 | chown -R bf2:bf2 /home/bf2/ 25 | chmod -R 700 /home/bf2/ 26 | 27 | exit 0 28 | -------------------------------------------------------------------------------- /local.env: -------------------------------------------------------------------------------- 1 | ENV_SERVER_NAME=bf2-docker 2 | ENV_DEMOS_URL=http://localhost:8000/demos 3 | ENV_RCON_PASSWORD=rconpw123 4 | ENV_BF2CCD_PASSWORD=bf2ccdpw123 --------------------------------------------------------------------------------