├── .gitignore ├── samp037svr_R2-1.tar.gz ├── bin ├── start.sh └── check_server.py ├── .env ├── server.cfg ├── docker-compose.yml ├── Dockerfile ├── README.md └── Makefile /.gitignore: -------------------------------------------------------------------------------- 1 | nohup.out 2 | server_log.txt 3 | samp03/ 4 | instances/ 5 | .token 6 | -------------------------------------------------------------------------------- /samp037svr_R2-1.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/krustowski/samp-server-docker/HEAD/samp037svr_R2-1.tar.gz -------------------------------------------------------------------------------- /bin/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # start.sh 4 | # samp-server-docker entrypoint 5 | # by krusty / 15. 1. 2020 6 | 7 | #trap "cd /mnt; rm -rf ${HOSTNAME}" SIGINT 8 | 9 | exec ${APP_ROOT}/samp03svr 10 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # 2 | # samp-server-docker 3 | # dot-ENV file constants 4 | # 5 | 6 | PROJECT_NAME=samp-server 7 | 8 | APP_ROOT=/srv/samp03 9 | TGZ_FILE=samp037svr_R2-1.tar.gz 10 | 11 | CONTAINER_NAME=gta-samp-server 12 | TAG=krustowski/samp-server-docker:latest 13 | 14 | EXTERNAL_PORT=7777 15 | -------------------------------------------------------------------------------- /server.cfg: -------------------------------------------------------------------------------- 1 | echo Starting GTA SA:MP Linux Server... 2 | lanmode 0 3 | rcon_password dsa$'@^%&[[SSD"444dsaS 4 | maxplayers 50 5 | port 7777 6 | hostname Dockerized SA:MP Linux Server! 7 | gamemode0 grandlarc 1 8 | filterscripts base gl_actions gl_property gl_realtime 9 | announce 0 10 | query 1 11 | weburl github.com/krustowski/samp-server-docker 12 | maxnpc 0 13 | onfoot_rate 40 14 | incar_rate 40 15 | weapon_rate 40 16 | stream_distance 300.0 17 | stream_rate 1000 18 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.6' 2 | 3 | services: 4 | samp-server: 5 | image: ${TAG} 6 | container_name: ${CONTAINER_NAME} 7 | build: 8 | context: . 9 | args: 10 | APP_ROOT: ${APP_ROOT} 11 | TGZ_FILE: ${TGZ_FILE} 12 | restart: unless-stopped 13 | ports: 14 | - ${EXTERNAL_PORT}:7777/tcp 15 | - ${EXTERNAL_PORT}:7777/udp 16 | volumes: 17 | - "./server.cfg:${APP_ROOT}/server.cfg" 18 | networks: 19 | - "samp-server-net" 20 | 21 | networks: 22 | samp-server-net: 23 | name: samp-server-net 24 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # samp-server-docker Dockerfile 2 | 3 | FROM debian:bullseye-slim 4 | 5 | ARG TGZ_FILE 6 | ARG APP_ROOT 7 | 8 | ENV TGZ_FILE ${TGZ_FILE} 9 | ENV APP_ROOT ${APP_ROOT} 10 | 11 | # 12 | # install environment + architecture 13 | # 14 | 15 | RUN dpkg --add-architecture i386 16 | RUN apt update && \ 17 | apt upgrade -yy && \ 18 | apt install -yy \ 19 | apt-utils \ 20 | libstdc++6 \ 21 | libc6:i386 \ 22 | libncurses5:i386 \ 23 | libstdc++6:i386 \ 24 | procps 25 | 26 | # 27 | # copy and extract samp-server file-structure 28 | # 29 | 30 | COPY ${TGZ_FILE} /tmp/ 31 | RUN [ ! -d "${APP_ROOT}" ] && cd /srv && tar xzvf /tmp/${TGZ_FILE} 32 | 33 | # 34 | # edit cfg 35 | # 36 | 37 | RUN sed -i 's|changeme|temporary_rcon_password_change_me!|' ${APP_ROOT}/server.cfg 38 | #RUN sed -i 's|announce 0|announce 1|' /srv/samp03/server.cfg 39 | 40 | # map server log to STDOUT => use `docker logs samp-server-name` to explore it 41 | RUN ln -sf /dev/stdout ${APP_ROOT}/server_log.txt 42 | 43 | # 44 | # run server 45 | # 46 | 47 | EXPOSE 7777 48 | COPY bin/start.sh /start.sh 49 | 50 | STOPSIGNAL SIGINT 51 | 52 | WORKDIR ${APP_ROOT} 53 | ENTRYPOINT ["/start.sh"] 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GTA San Andreas Multiplayer (SA:MP) Linux Server in Docker (~128 MB) 2 | 3 | Very simple docker image for running GTA SA:MP Server 0.37 R2-1. 4 | 5 | Docker Hub: 6 | + https://hub.docker.com/r/krustowski/samp-server-docker 7 | 8 | Original Linux server tar.gz can be found here (included in this repo): 9 | + http://files.sa-mp.com/samp037svr_R2-1.tar.gz 10 | + https://www.sa-mp.com/download.php 11 | 12 | 13 | ## Run image (using Makefile) 14 | 15 | The easiest way is using Makefile (`gnumake`) and .env (core constants) files: 16 | 17 | ```shell 18 | # edit the constants (eg. EXTERNAL_PORT) 19 | vi .env 20 | 21 | # show possible commands (targets) 22 | make 23 | 24 | # build the image and run the container locally 25 | make deploy 26 | 27 | # test server connection using python package 'samp-client' (installed by pip) 28 | # credit: https://github.com/mick88/samp-client 29 | make test 30 | bin/check_server.py 127.0.0.1 7777 31 | 32 | # show game logs (continuous fetching) 33 | make logs 34 | 35 | # stop/delete the server 36 | make stop 37 | ``` 38 | 39 | 40 | ## Run image "manually" 41 | 42 | Pull the image from Docker Hub and run it 43 | 44 | ```shell 45 | docker run -d --rm -p 7777:7777 -p 7777:7777/udp --name samp-server krustowski/samp-server-docker 46 | ``` 47 | 48 | It it vital to forward both TCP and UDP ports! 49 | 50 | 51 | ## Build image 52 | 53 | Image can be built using Dockerfile as well on the local machine: 54 | 55 | ```shell 56 | docker build . -t samp-server-built-image 57 | docker run -d -p 7777:7777 -p 7777:7777/udp --name samp-server2 samp-server-built-image 58 | ``` 59 | 60 | 61 | ## TODO (nice-to-have) 62 | 63 | - custom volume mounting (custom configs, gamemodes, env stuff) 64 | - fail2ban implementation for RCON brute-force attacks 65 | 66 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # samp-server-docker Makefile 3 | # 4 | 5 | -include .env 6 | 7 | RUN_FROM_MAKEFILE?=1 8 | EXTERNAL_PORT?=7777 9 | 10 | # define standard colors 11 | # https://gist.github.com/rsperl/d2dfe88a520968fbc1f49db0a29345b9 12 | ifneq (,$(findstring xterm,${TERM})) 13 | BLACK := $(shell tput -Txterm setaf 0) 14 | RED := $(shell tput -Txterm setaf 1) 15 | GREEN := $(shell tput -Txterm setaf 2) 16 | YELLOW := $(shell tput -Txterm setaf 3) 17 | LIGHTPURPLE := $(shell tput -Txterm setaf 4) 18 | PURPLE := $(shell tput -Txterm setaf 5) 19 | BLUE := $(shell tput -Txterm setaf 6) 20 | WHITE := $(shell tput -Txterm setaf 7) 21 | RESET := $(shell tput -Txterm sgr0) 22 | else 23 | BLACK := "" 24 | RED := "" 25 | GREEN := "" 26 | YELLOW := "" 27 | LIGHTPURPLE := "" 28 | PURPLE := "" 29 | BLUE := "" 30 | WHITE := "" 31 | RESET := "" 32 | endif 33 | 34 | export 35 | 36 | 37 | # 38 | # targets 39 | # 40 | 41 | all: info 42 | 43 | info: 44 | @echo -e "\n ${BLUE}Makefile for SA:MP Linux Server${RESET}\n" 45 | 46 | @echo -e " ${YELLOW}make deploy${RESET} -- rebuild image + recreate container" 47 | @echo -e " ${YELLOW}make stop${RESET} -- stop and destroy all linked containers\n" 48 | 49 | @echo -e " ${YELLOW}make logs${RESET} -- show docker logs" 50 | @echo -e " ${YELLOW}make test${RESET} -- test the connection to SA:MP server\n" 51 | 52 | deploy: build run 53 | 54 | build: 55 | @echo -e "\n ${BLUE}Building the image ...${RESET}\n" 56 | @docker-compose build 57 | 58 | run: 59 | @echo -e "\n ${BLUE}Running brand-new container(s) ...${RESET}\n" 60 | @docker-compose up --detach --force-recreate 61 | 62 | stop: 63 | @echo -e "\n ${BLUE}Stopping and removing linked container(s) ...${RESET}\n" 64 | @docker-compose down 65 | 66 | test: 67 | @echo -e "\n ${BLUE}Testing server (def. localhost:7777 -- specify by EXTERNAL_PORT) ...${RESET}\n" 68 | @pip install samp-client >/dev/null && \ 69 | bin/check_server.py localhost ${EXTERNAL_PORT} 70 | 71 | logs: 72 | @echo -e "\n${YELLOW} Fetching container's logs (CTRL-C to exit)... ${RESET}\n" 73 | @docker logs ${CONTAINER_NAME} -f 74 | 75 | push: 76 | @echo -e "\n ${BLUE}Pushing build image to Docker Hub ...${RESET}\n" 77 | @docker login && \ 78 | docker push ${TAG} 79 | 80 | -------------------------------------------------------------------------------- /bin/check_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Usage: `python example.py (address=localhost) (port=7777) (rcon_password)` 3 | # Example script demonstrating the use of this library 4 | # 5 | 6 | # credit: https://github.com/mick88/samp-client 7 | 8 | import sys 9 | from builtins import input 10 | 11 | from samp_client.client import SampClient 12 | 13 | 14 | def info(client): 15 | print("""Server Info: 16 | Password: {info.password} 17 | Players: {info.players}/{info.max_players} 18 | Hostname: {info.hostname} 19 | Gamemode: {info.gamemode} 20 | Language: {info.language} 21 | """.format( 22 | info=client.get_server_info(), 23 | )) 24 | 25 | 26 | def rules(client): 27 | print("Server Rules:") 28 | for rule in client.get_server_rules(): 29 | print("{rule.name}: {rule.value}".format( 30 | rule=rule, 31 | )) 32 | 33 | 34 | def clients(client): 35 | print("Connected Clients") 36 | print("Name | Score".format(client=client)) 37 | print("==================================") 38 | for client in client.get_server_clients(): 39 | print("{client.name:26} | {client.score}".format(client=client)) 40 | 41 | 42 | def details(client): 43 | print("Detailed Clients") 44 | print(" ID |Name | Score | Ping".format(client=client)) 45 | print("===============================================") 46 | for client in client.get_server_clients_detailed(): 47 | print("{client.id:3} | {client.name:26} | {client.score:5} | {client.ping:4}".format(client=client)) 48 | 49 | 50 | def rcon(client): 51 | if client.rcon_password is None: 52 | client.rcon_password = input('RCON password:') 53 | print('Enter rcon commands or leave blank to exit. Example: cmdlist') 54 | while True: 55 | command = input('RCON: ') 56 | if not command: 57 | return 58 | for line in client.send_rcon_command(command): 59 | print(line) 60 | 61 | 62 | def main(args): 63 | with SampClient(*args) as client: 64 | if not client.is_online(): 65 | print('Server {}:{} is offline'.format(*args)) 66 | exit(1) 67 | server_info = client.get_server_info() 68 | print("""Connected to {info.hostname}. 69 | Currently {info.players}/{info.max_players} players online. 70 | 71 | Select one of the options: 72 | 73 | i. Server Info 74 | r. Server Rules 75 | c. Connected clients 76 | d. Detailed clients 77 | x. RCON 78 | """.format( 79 | info=server_info, 80 | )) 81 | 82 | options = { 83 | 'i': info, 84 | 'r': rules, 85 | 'c': clients, 86 | 'd': details, 87 | 'x': rcon, 88 | } 89 | option = input('Select option: ') 90 | if option in options: options[option](client) 91 | else: print('Unknown option, bye!') 92 | 93 | 94 | if len(sys.argv) < 3: 95 | print('Usage: python example.py [server_address] [port]') 96 | exit(1) 97 | main(sys.argv[1:]) 98 | --------------------------------------------------------------------------------