This commit is contained in:
CroneKorkN 2025-08-10 17:59:48 +02:00
parent 5fd775d855
commit 9786d6569d
Signed by: cronekorkn
SSH key fingerprint: SHA256:v0410ZKfuO1QHdgKBsdQNF64xmTxOF8osF1LIqwTcVw
12 changed files with 629 additions and 337 deletions

View file

@ -1,58 +1,55 @@
https://developer.valvesoftware.com/wiki/List_of_L4D2_Cvars # https://github.com/SirPlease/L4D2-Competitive-Rework/blob/master/Dedicated%20Server%20Install%20Guide/README.md
Dead Center c1m1_hotel mkdir /opt/steam /tmp/dumps
Dead Center c1m2_streets useradd -M -d /opt/steam -s /bin/bash steam
Dead Center c1m3_mall chown steam:steam /opt/steam /tmp/dumps
Dead Center c1m4_atrium dpkg --add-architecture i386
Dark Carnival c2m1_highway apt update
Dark Carnival c2m2_fairgrounds apt install libc6:i386 lib32z1
Dark Carnival c2m3_coaster sudo su - steam -s /bin/bash
Dark Carnival c2m4_barns
Dark Carnival c2m5_concert #--------
Swamp Fever c3m1_plankcountry
Swamp Fever c3m2_swamp wget http://media.steampowered.com/installer/steamcmd_linux.tar.gz
Swamp Fever c3m3_shantytown tar -xvzf steamcmd_linux.tar.gz
Swamp Fever c3m4_plantation
Hard Rain c4m1_milltown_a # fix: /opt/steam/.steam/sdk32/steamclient.so: cannot open shared object file: No such file or directory
Hard Rain c4m2_sugarmill_a mkdir /opt/steam/.steam && ln -s /opt/steam/linux32 /opt/steam/.steam/sdk32
Hard Rain c4m3_sugarmill_b
Hard Rain c4m4_milltown_b # erst die windows deps zu installieren scheint ein workaround für x64 zu sein?
Hard Rain c4m5_milltown_escape ./steamcmd.sh \
The Parish c5m1_waterfront_sndscape +force_install_dir /opt/steam/left4dead2 \
The Parish c5m1_waterfront +login anonymous \
The Parish c5m2_park +@sSteamCmdForcePlatformType windows \
The Parish c5m3_cemetery +app_update 222860 validate \
The Parish c5m4_quarter +quit
The Parish c5m5_bridge ./steamcmd.sh \
The Passing c6m1_riverbank +force_install_dir /opt/steam/left4dead2 \
The Passing c6m2_bedlam +login anonymous \
The Passing c6m3_port +@sSteamCmdForcePlatformType linux \
The Sacrifice c7m1_docks +app_update 222860 validate \
The Sacrifice c7m2_barge +quit
The Sacrifice c7m3_port
No Mercy c8m1_apartment # download admin system
No Mercy c8m2_subway wget -4 https://git.sublimity.de/cronekorkn/steam-workshop-downloader/raw/branch/master/steam-workshop-download
No Mercy c8m3_sewers chmod +x steam-workshop-download
No Mercy c8m4_interior ./steam-workshop-download 2524204971 --out /opt/steam/left4dead2/left4dead2/addons
No Mercy c8m5_rooftop mkdir -p "/opt/steam/left4dead2/left4dead2/ems/admin system"
Crash Course c9m1_alleys echo "STEAM_1:0:12376499" > "/opt/steam/left4dead2/left4dead2/ems/admin system/admins.txt"
Crash Course c9m2_lots
Death Toll c10m1_caves /opt/steam/left4dead2/srcds_run -game left4dead2 -ip 0.0.0.0 -port 27015 +map c1m1_hotel
Death Toll c10m2_drainage
Death Toll c10m3_ranchhouse
Death Toll c10m4_mainstreet cat <<'EOF' > /opt/steam/left4dead2/left4dead2/cfg/server.cfg
Death Toll c10m5_houseboat hostname "CKNs Server"
Dead Air c11m1_greenhouse motd_enabled 0
Dead Air c11m2_offices
Dead Air c11m3_garage sv_steamgroup "38347879"
Dead Air c11m4_terminal #sv_steamgroup_exclusive 0
Dead Air c11m5_runway
Blood Harvest c12m1_hilltop sv_minrate 60000
Blood Harvest c12m2_traintunnel sv_maxrate 0
Blood Harvest c12m3_bridge net_splitpacket_maxrate 60000
Blood Harvest c12m4_barn
Blood Harvest c12m5_cornfield sv_hibernate_when_empty 0
Cold Stream c13m1_alpinecreek EOF
Cold Stream c13m2_southpinestream
Cold Stream c13m3_memorialbridge
Cold Stream c13m4_cutthroatcreek

View file

@ -1,40 +0,0 @@
hostname "CroneKorkN : ${name}"
sv_contact "admin@sublimity.de"
sv_steamgroup "${','.join(steamgroups)}"
rcon_password "${rcon_password}"
motd_enabled 0
sv_cheats 1
sv_consistency 0
sv_lan 0
sv_allow_lobby_connect_only 0
sv_gametypes "coop,realism,survival,versus,teamversus,scavenge,teamscavenge"
sv_minrate 30000
sv_maxrate 60000
sv_mincmdrate 66
sv_maxcmdrate 101
sv_logsdir "logs-${name}" //Folder in the game directory where server logs will be stored.
log on //Creates a logfile (on | off)
sv_logecho 0 //default 0; Echo log information to the console.
sv_logfile 1 //default 1; Log server information in the log file.
sv_log_onefile 0 //default 0; Log server information to only one file.
sv_logbans 1 //default 0;Log server bans in the server logs.
sv_logflush 0 //default 0; Flush the log files to disk on each write (slow).

View file

@ -1,122 +1,183 @@
assert node.has_bundle('steam') and node.has_bundle('steam-workshop-download') from shlex import quote
def steam_run(cmd):
return f'su - steam -c {quote(cmd)}'
users = {
'steam': {
'home': '/opt/steam',
},
}
directories = { directories = {
'/opt/steam/left4dead2-servers': { '/opt/steam': {
'owner': 'steam', 'owner': 'steam',
'group': 'steam', 'group': 'steam',
'mode': '0755',
'purge': True,
}, },
# Current zfs doesnt support zfs upperdir. The support was added in October 2022. Move upperdir - unused anyway - '/opt/steam/.steam': {
# to another dir. Also move workdir alongside it, as it has to be on same fs.
'/opt/steam-zfs-overlay-workarounds': {
'owner': 'steam', 'owner': 'steam',
'group': 'steam', 'group': 'steam',
'mode': '0755', },
'purge': True, '/opt/left4dead2': {
'owner': 'steam',
'group': 'steam',
},
'/opt/left4dead2/left4dead2/ems/admin system': {
'owner': 'steam',
'group': 'steam',
},
'/opt/left4dead2/left4dead2/addons': {
'owner': 'steam',
'group': 'steam',
},
'/tmp/dumps': {
'owner': 'steam',
'group': 'steam',
'mode': '1770',
}, },
} }
# /opt/steam/steam/.steam/sdk32/steamclient.so: cannot open shared object file: No such file or directory
symlinks = { symlinks = {
'/opt/steam/steam/.steam/sdk32': { '/opt/steam/.steam/sdk32': {
'target': '/opt/steam/steam/linux32', 'target': '/opt/steam/linux32',
'owner': 'steam', 'owner': 'steam',
'group': 'steam', 'group': 'steam',
} },
} }
# files = {
# SERVERS '/opt/steam-workshop-download': {
# 'content_type': 'download',
'source': 'https://git.sublimity.de/cronekorkn/steam-workshop-downloader/raw/branch/master/steam-workshop-download',
'mode': '755',
},
'/opt/left4dead2/left4dead2/ems/admin system/admins.txt': {
'unless': 'test -f /opt/left4dead2/left4dead2/ems/admin system/admins.txt',
'content': 'STEAM_1:0:12376499',
'owner': 'steam',
'group': 'steam',
},
}
for name, config in node.metadata.get('left4dead2/servers').items(): actions = {
'dpkg_add_architecture': {
#overlay 'command': 'dpkg --add-architecture i386',
directories[f'/opt/steam/left4dead2-servers/{name}'] = { 'unless': 'dpkg --print-foreign-architectures | grep -q i386',
'owner': 'steam',
'group': 'steam',
}
directories[f'/opt/steam-zfs-overlay-workarounds/{name}/upper'] = {
'owner': 'steam',
'group': 'steam',
}
directories[f'/opt/steam-zfs-overlay-workarounds/{name}/workdir'] = {
'owner': 'steam',
'group': 'steam',
}
# conf
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/cfg/server.cfg'] = {
'content_type': 'mako',
'source': 'server.cfg',
'context': {
'name': name,
'steamgroups': node.metadata.get('left4dead2/steamgroups'),
'rcon_password': config['rcon_password'],
},
'owner': 'steam',
'group': 'steam',
'triggers': [ 'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart', 'action:apt_update',
], ],
} 'needed_by': [
'pkg_apt:libc6_i386',
# service
svc_systemd[f'left4dead2-{name}.service'] = {
'needs': [
f'file:/opt/steam/left4dead2-servers/{name}/left4dead2/cfg/server.cfg',
f'file:/usr/local/lib/systemd/system/left4dead2-{name}.service',
], ],
} },
'download_steam': {
# 'command': steam_run('wget http://media.steampowered.com/installer/steamcmd_linux.tar.gz -P /opt/steam'),
# ADDONS 'unless': steam_run('test -f /opt/steam/steamcmd_linux.tar.gz'),
# 'needs': {
'pkg_apt:libc6_i386',
# base 'directory:/opt/steam',
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons/readme.txt'] = {
'content_type': 'any',
'owner': 'steam',
'group': 'steam',
}
directories[f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons'] = {
'owner': 'steam',
'group': 'steam',
'purge': True,
'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart',
],
}
for id in [
*config.get('workshop', []),
*node.metadata.get('left4dead2/workshop'),
]:
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons/{id}.vpk'] = {
'content_type': 'any',
'owner': 'steam',
'group': 'steam',
'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart',
],
} }
},
'extract_steamcmd': {
'command': steam_run('tar -xvzf /opt/steam/steamcmd_linux.tar.gz -C /opt/steam'),
'unless': steam_run('test -f /opt/steam/steamcmd.sh'),
'needs': {
'action:download_steam',
}
},
}
# admin system for addon_id in [2524204971]:
actions[f'download-left4dead2-addon-{addon_id}'] = {
'command': steam_run(f'/opt/steam-workshop-download {addon_id} --out /opt/left4dead2/left4dead2/addons'),
'unless': steam_run(f'test -f /opt/left4dead2/left4dead2/addons/{addon_id}.vpk'),
'needs': {
'directory:/opt/left4dead2/left4dead2/addons',
},
'needed_by': {
'tag:left4dead2-servers',
},
}
directories[f'/opt/steam/left4dead2-servers/{name}/left4dead2/ems/admin system'] = { svc_systemd = {
'owner': 'steam', 'left4dead2-install.service': {
'group': 'steam', 'enabled': True,
'mode': '0755', 'running': False,
'triggers': [ 'needs': {
f'svc_systemd:left4dead2-{name}.service:restart', 'file:/usr/local/lib/systemd/system/left4dead2-install.service',
], },
} },
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/ems/admin system/admins.txt'] = { }
'owner': 'steam',
'group': 'steam', for server_name, server_config in node.metadata.get('left4dead2/servers', {}).items():
'mode': '0755', svc_systemd[f'left4dead2-{server_name}.service'] = {
'content': '\n'.join(sorted(node.metadata.get('left4dead2/admins'))), 'enabled': True,
'triggers': [ 'running': True,
f'svc_systemd:left4dead2-{name}.service:restart', 'tags': {
], 'left4dead2-servers',
},
'needs': {
'svc_systemd:left4dead2-install.service',
f'file:/usr/local/lib/systemd/system/left4dead2-{server_name}.service',
}
} }
# # https://github.com/SirPlease/L4D2-Competitive-Rework/blob/master/Dedicated%20Server%20Install%20Guide/README.md
# mkdir /opt/steam /tmp/dumps
# useradd -M -d /opt/steam -s /bin/bash steam
# chown steam:steam /opt/steam /tmp/dumps
# dpkg --add-architecture i386
# apt update
# apt install libc6:i386 lib32z1
# sudo su - steam -s /bin/bash
# #--------
# wget http://media.steampowered.com/installer/steamcmd_linux.tar.gz
# tar -xvzf steamcmd_linux.tar.gz
# # fix: /opt/steam/.steam/sdk32/steamclient.so: cannot open shared object file: No such file or directory
# mkdir /opt/steam/.steam && ln -s /opt/steam/linux32 /opt/steam/.steam/sdk32
# # erst die windows deps zu installieren scheint ein workaround für x64 zu sein?
# ./steamcmd.sh \
# +force_install_dir /opt/steam/left4dead2 \
# +login anonymous \
# +@sSteamCmdForcePlatformType windows \
# +app_update 222860 validate \
# +quit
# ./steamcmd.sh \
# +force_install_dir /opt/steam/left4dead2 \
# +login anonymous \
# +@sSteamCmdForcePlatformType linux \
# +app_update 222860 validate \
# +quit
# # download admin system
# wget -4 https://git.sublimity.de/cronekorkn/steam-workshop-downloader/raw/branch/master/steam-workshop-download
# chmod +x steam-workshop-download
# ./steam-workshop-download 2524204971 --out /opt/steam/left4dead2/left4dead2/addons
# mkdir -p "/opt/steam/left4dead2/left4dead2/ems/admin system"
# echo "STEAM_1:0:12376499" > "/opt/steam/left4dead2/left4dead2/ems/admin system/admins.txt"
# /opt/steam/left4dead2/srcds_run -game left4dead2 -ip 0.0.0.0 -port 27015 +map c1m1_hotel
# cat <<'EOF' > /opt/steam/left4dead2/left4dead2/cfg/server.cfg
# hostname "CKNs Server"
# motd_enabled 0
# sv_steamgroup "38347879"
# #sv_steamgroup_exclusive 0
# sv_minrate 60000
# sv_maxrate 0
# net_splitpacket_maxrate 60000
# sv_hibernate_when_empty 0
# EOF

View file

@ -1,101 +1,98 @@
assert node.has_bundle('steam') from re import match
from shlex import quote
defaults = { defaults = {
'steam': { 'apt': {
'games': { 'packages': {
'left4dead2': 222860, 'libc6_i386': {}, # installs libc6:i386
'lib32z1': {},
'unzip': {},
}, },
}, },
'left4dead2': { 'left4dead2': {
'servers': {}, 'servers': {},
'admins': set(), },
'workshop': set(), 'nftables': {
'input': {
'udp dport { 27005, 27020 } accept',
},
}, },
} }
@metadata_reactor.provides( @metadata_reactor.provides(
'left4dead2/servers', 'nftables/input',
) )
def rconn_password(metadata): def nftables(metadata):
# only works from localhost! ports = sorted(str(config["port"]) for config in metadata.get('left4dead2/servers', {}).values())
return { return {
'left4dead2': { 'nftables': {
'servers': { 'input': {
server: { f'ip protocol {{ tcp, udp }} th dport {{ {", ".join(ports)} }} accept'
'rcon_password': repo.vault.password_for(f'{node.name} left4dead2 {server} rcon', length=24), },
} },
for server in metadata.get('left4dead2/servers') }
@metadata_reactor.provides(
'systemd/units',
)
def initial_unit(metadata):
install_command = (
'/opt/steam/steamcmd.sh '
'+force_install_dir /opt/left4dead2 '
'+login anonymous '
'+@sSteamCmdForcePlatformType {platform} '
'+app_update 222860 validate '
'+quit '
)
return {
'systemd': {
'units': {
'left4dead2-install.service': {
'Unit': {
'Description': 'install or update left4dead2',
'After': 'network-online.target',
},
'Service': {
'Type': 'oneshot',
'RemainAfterExit': 'yes',
'User': 'steam',
'Group': 'steam',
'WorkingDirectory': '/opt/steam',
'ExecStartPre': install_command.format(platform='windows'),
'ExecStart': install_command.format(platform='linux'),
},
'Install': {
'WantedBy': {'multi-user.target'},
},
},
}, },
}, },
} }
@metadata_reactor.provides( @metadata_reactor.provides(
'steam-workshop-download',
'systemd/units', 'systemd/units',
) )
def server_units(metadata): def server_units(metadata):
units = {} units = {}
workshop = {}
for name, config in metadata.get('left4dead2/servers').items(): for name, config in metadata.get('left4dead2/servers').items():
# mount overlay assert match(r'^[A-z0-9-_-]+$', name)
mountpoint = f'/opt/steam/left4dead2-servers/{name}'
mount_unit_name = mountpoint[1:].replace('-', '\\x2d').replace('/', '-') + '.mount'
units[mount_unit_name] = {
'Unit': {
'Description': f"Mount left4dead2 server {name} overlay",
'Conflicts': {'umount.target'},
'Before': {'umount.target'},
},
'Mount': {
'What': 'overlay',
'Where': mountpoint,
'Type': 'overlay',
'Options': ','.join([
'auto',
'lowerdir=/opt/steam/left4dead2',
f'upperdir=/opt/steam-zfs-overlay-workarounds/{name}/upper',
f'workdir=/opt/steam-zfs-overlay-workarounds/{name}/workdir',
]),
},
'Install': {
'RequiredBy': {
f'left4dead2-{name}.service',
},
},
}
# individual workshop
workshop_ids = config.get('workshop', set()) | metadata.get('left4dead2/workshop', set())
if workshop_ids:
workshop[f'left4dead2-{name}'] = {
'ids': workshop_ids,
'path': f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons',
'user': 'steam',
'requires': {
mount_unit_name,
},
'required_by': {
f'left4dead2-{name}.service',
},
}
# left4dead2 server unit
units[f'left4dead2-{name}.service'] = { units[f'left4dead2-{name}.service'] = {
'Unit': { 'Unit': {
'Description': f'left4dead2 server {name}', 'Description': f'left4dead2 server {name}',
'After': {'steam-update.service'}, 'After': {'left4dead2-install.service'},
'Requires': {'steam-update.service'}, 'Requires': {'left4dead2-install.service'},
}, },
'Service': { 'Service': {
'User': 'steam', 'User': 'steam',
'Group': 'steam', 'Group': 'steam',
'WorkingDirectory': f'/opt/steam/left4dead2-servers/{name}', 'WorkingDirectory': '/opt/left4dead2',
'ExecStart': f'/opt/steam/left4dead2-servers/{name}/srcds_run -port {config["port"]} +exec server.cfg', 'ExecStart': f'/opt/left4dead2/srcds_run -port {config["port"]} +exec server_{name}.cfg',
'Restart': 'on-failure', 'Restart': 'on-failure',
}, },
'Install': { 'Install': {
@ -104,24 +101,7 @@ def server_units(metadata):
} }
return { return {
'steam-workshop-download': workshop,
'systemd': { 'systemd': {
'units': units, 'units': units,
}, },
} }
@metadata_reactor.provides(
'nftables/input',
)
def firewall(metadata):
ports = set(str(server['port']) for server in metadata.get('left4dead2/servers').values())
return {
'nftables': {
'input': {
f"tcp dport {{ {', '.join(sorted(ports))} }} accept",
f"udp dport {{ {', '.join(sorted(ports))} }} accept",
},
},
}

View file

@ -0,0 +1,58 @@
https://developer.valvesoftware.com/wiki/List_of_L4D2_Cvars
Dead Center c1m1_hotel
Dead Center c1m2_streets
Dead Center c1m3_mall
Dead Center c1m4_atrium
Dark Carnival c2m1_highway
Dark Carnival c2m2_fairgrounds
Dark Carnival c2m3_coaster
Dark Carnival c2m4_barns
Dark Carnival c2m5_concert
Swamp Fever c3m1_plankcountry
Swamp Fever c3m2_swamp
Swamp Fever c3m3_shantytown
Swamp Fever c3m4_plantation
Hard Rain c4m1_milltown_a
Hard Rain c4m2_sugarmill_a
Hard Rain c4m3_sugarmill_b
Hard Rain c4m4_milltown_b
Hard Rain c4m5_milltown_escape
The Parish c5m1_waterfront_sndscape
The Parish c5m1_waterfront
The Parish c5m2_park
The Parish c5m3_cemetery
The Parish c5m4_quarter
The Parish c5m5_bridge
The Passing c6m1_riverbank
The Passing c6m2_bedlam
The Passing c6m3_port
The Sacrifice c7m1_docks
The Sacrifice c7m2_barge
The Sacrifice c7m3_port
No Mercy c8m1_apartment
No Mercy c8m2_subway
No Mercy c8m3_sewers
No Mercy c8m4_interior
No Mercy c8m5_rooftop
Crash Course c9m1_alleys
Crash Course c9m2_lots
Death Toll c10m1_caves
Death Toll c10m2_drainage
Death Toll c10m3_ranchhouse
Death Toll c10m4_mainstreet
Death Toll c10m5_houseboat
Dead Air c11m1_greenhouse
Dead Air c11m2_offices
Dead Air c11m3_garage
Dead Air c11m4_terminal
Dead Air c11m5_runway
Blood Harvest c12m1_hilltop
Blood Harvest c12m2_traintunnel
Blood Harvest c12m3_bridge
Blood Harvest c12m4_barn
Blood Harvest c12m5_cornfield
Cold Stream c13m1_alpinecreek
Cold Stream c13m2_southpinestream
Cold Stream c13m3_memorialbridge
Cold Stream c13m4_cutthroatcreek

View file

@ -0,0 +1,40 @@
hostname "CroneKorkN : ${name}"
sv_contact "admin@sublimity.de"
sv_steamgroup "${','.join(steamgroups)}"
rcon_password "${rcon_password}"
motd_enabled 0
sv_cheats 1
sv_consistency 0
sv_lan 0
sv_allow_lobby_connect_only 0
sv_gametypes "coop,realism,survival,versus,teamversus,scavenge,teamscavenge"
sv_minrate 30000
sv_maxrate 60000
sv_mincmdrate 66
sv_maxcmdrate 101
sv_logsdir "logs-${name}" //Folder in the game directory where server logs will be stored.
log on //Creates a logfile (on | off)
sv_logecho 0 //default 0; Echo log information to the console.
sv_logfile 1 //default 1; Log server information in the log file.
sv_log_onefile 0 //default 0; Log server information to only one file.
sv_logbans 1 //default 0;Log server bans in the server logs.
sv_logflush 0 //default 0; Flush the log files to disk on each write (slow).

View file

@ -0,0 +1,122 @@
assert node.has_bundle('steam') and node.has_bundle('steam-workshop-download')
directories = {
'/opt/steam/left4dead2-servers': {
'owner': 'steam',
'group': 'steam',
'mode': '0755',
'purge': True,
},
# Current zfs doesnt support zfs upperdir. The support was added in October 2022. Move upperdir - unused anyway -
# to another dir. Also move workdir alongside it, as it has to be on same fs.
'/opt/steam-zfs-overlay-workarounds': {
'owner': 'steam',
'group': 'steam',
'mode': '0755',
'purge': True,
},
}
# /opt/steam/steam/.steam/sdk32/steamclient.so: cannot open shared object file: No such file or directory
symlinks = {
'/opt/steam/steam/.steam/sdk32': {
'target': '/opt/steam/steam/linux32',
'owner': 'steam',
'group': 'steam',
}
}
#
# SERVERS
#
for name, config in node.metadata.get('left4dead2/servers').items():
#overlay
directories[f'/opt/steam/left4dead2-servers/{name}'] = {
'owner': 'steam',
'group': 'steam',
}
directories[f'/opt/steam-zfs-overlay-workarounds/{name}/upper'] = {
'owner': 'steam',
'group': 'steam',
}
directories[f'/opt/steam-zfs-overlay-workarounds/{name}/workdir'] = {
'owner': 'steam',
'group': 'steam',
}
# conf
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/cfg/server.cfg'] = {
'content_type': 'mako',
'source': 'server.cfg',
'context': {
'name': name,
'steamgroups': node.metadata.get('left4dead2/steamgroups'),
'rcon_password': config['rcon_password'],
},
'owner': 'steam',
'group': 'steam',
'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart',
],
}
# service
svc_systemd[f'left4dead2-{name}.service'] = {
'needs': [
f'file:/opt/steam/left4dead2-servers/{name}/left4dead2/cfg/server.cfg',
f'file:/usr/local/lib/systemd/system/left4dead2-{name}.service',
],
}
#
# ADDONS
#
# base
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons/readme.txt'] = {
'content_type': 'any',
'owner': 'steam',
'group': 'steam',
}
directories[f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons'] = {
'owner': 'steam',
'group': 'steam',
'purge': True,
'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart',
],
}
for id in [
*config.get('workshop', []),
*node.metadata.get('left4dead2/workshop'),
]:
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons/{id}.vpk'] = {
'content_type': 'any',
'owner': 'steam',
'group': 'steam',
'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart',
],
}
# admin system
directories[f'/opt/steam/left4dead2-servers/{name}/left4dead2/ems/admin system'] = {
'owner': 'steam',
'group': 'steam',
'mode': '0755',
'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart',
],
}
files[f'/opt/steam/left4dead2-servers/{name}/left4dead2/ems/admin system/admins.txt'] = {
'owner': 'steam',
'group': 'steam',
'mode': '0755',
'content': '\n'.join(sorted(node.metadata.get('left4dead2/admins'))),
'triggers': [
f'svc_systemd:left4dead2-{name}.service:restart',
],
}

View file

@ -0,0 +1,127 @@
assert node.has_bundle('steam')
from shlex import quote
defaults = {
'steam': {
'games': {
'left4dead2': 222860,
},
},
'left4dead2': {
'servers': {},
'admins': set(),
'workshop': set(),
},
}
@metadata_reactor.provides(
'left4dead2/servers',
)
def rconn_password(metadata):
# only works from localhost!
return {
'left4dead2': {
'servers': {
server: {
'rcon_password': repo.vault.password_for(f'{node.name} left4dead2 {server} rcon', length=24),
}
for server in metadata.get('left4dead2/servers')
},
},
}
@metadata_reactor.provides(
'steam-workshop-download',
'systemd/units',
)
def server_units(metadata):
units = {}
workshop = {}
for name, config in metadata.get('left4dead2/servers').items():
# mount overlay
mountpoint = f'/opt/steam/left4dead2-servers/{name}'
mount_unit_name = mountpoint[1:].replace('-', '\\x2d').replace('/', '-') + '.mount'
units[mount_unit_name] = {
'Unit': {
'Description': f"Mount left4dead2 server {name} overlay",
'Conflicts': {'umount.target'},
'Before': {'umount.target'},
},
'Mount': {
'What': 'overlay',
'Where': mountpoint,
'Type': 'overlay',
'Options': ','.join([
'auto',
'lowerdir=/opt/steam/left4dead2',
f'upperdir=/opt/steam-zfs-overlay-workarounds/{name}/upper',
f'workdir=/opt/steam-zfs-overlay-workarounds/{name}/workdir',
]),
},
'Install': {
'RequiredBy': {
f'left4dead2-{name}.service',
},
},
}
# individual workshop
workshop_ids = config.get('workshop', set()) | metadata.get('left4dead2/workshop', set())
if workshop_ids:
workshop[f'left4dead2-{name}'] = {
'ids': workshop_ids,
'path': f'/opt/steam/left4dead2-servers/{name}/left4dead2/addons',
'user': 'steam',
'requires': {
mount_unit_name,
},
'required_by': {
f'left4dead2-{name}.service',
},
}
# left4dead2 server unit
units[f'left4dead2-{name}.service'] = {
'Unit': {
'Description': f'left4dead2 server {name}',
'After': {'steam-update.service'},
'Requires': {'steam-update.service'},
},
'Service': {
'User': 'steam',
'Group': 'steam',
'WorkingDirectory': f'/opt/steam/left4dead2-servers/{name}',
'ExecStart': f'/opt/steam/left4dead2-servers/{name}/srcds_run -port {config["port"]} +exec server.cfg',
'Restart': 'on-failure',
},
'Install': {
'WantedBy': {'multi-user.target'},
},
}
return {
'steam-workshop-download': workshop,
'systemd': {
'units': units,
},
}
@metadata_reactor.provides(
'nftables/input',
)
def firewall(metadata):
ports = set(str(server['port']) for server in metadata.get('left4dead2/servers').values())
return {
'nftables': {
'input': {
f"tcp dport {{ {', '.join(sorted(ports))} }} accept",
f"udp dport {{ {', '.join(sorted(ports))} }} accept",
},
},
}

View file

@ -1,55 +0,0 @@
# https://github.com/SirPlease/L4D2-Competitive-Rework/blob/master/Dedicated%20Server%20Install%20Guide/README.md
mkdir /opt/steam /tmp/dumps
useradd -M -d /opt/steam -s /bin/bash steam
chown steam:steam /opt/steam /tmp/dumps
dpkg --add-architecture i386
apt update
apt install libc6:i386 lib32z1
sudo su - steam -s /bin/bash
#--------
wget http://media.steampowered.com/installer/steamcmd_linux.tar.gz
tar -xvzf steamcmd_linux.tar.gz
# fix: /opt/steam/.steam/sdk32/steamclient.so: cannot open shared object file: No such file or directory
mkdir /opt/steam/.steam && ln -s /opt/steam/linux32 /opt/steam/.steam/sdk32
# erst die windows deps zu installieren scheint ein workaround für x64 zu sein?
./steamcmd.sh \
+force_install_dir /opt/steam/left4dead2 \
+login anonymous \
+@sSteamCmdForcePlatformType windows \
+app_update 222860 validate \
+quit
./steamcmd.sh \
+force_install_dir /opt/steam/left4dead2 \
+login anonymous \
+@sSteamCmdForcePlatformType linux \
+app_update 222860 validate \
+quit
# download admin system
wget -4 https://git.sublimity.de/cronekorkn/steam-workshop-downloader/raw/branch/master/steam-workshop-download
chmod +x steam-workshop-download
./steam-workshop-download 2524204971 --out /opt/steam/left4dead2/left4dead2/addons
mkdir -p "/opt/steam/left4dead2/left4dead2/ems/admin system"
echo "STEAM_1:0:12376499" > "/opt/steam/left4dead2/left4dead2/ems/admin system/admins.txt"
/opt/steam/left4dead2/srcds_run -game left4dead2 -ip 0.0.0.0 -port 27015 +map c1m1_hotel
cat <<'EOF' > /opt/steam/left4dead2/left4dead2/cfg/server.cfg
hostname "CKNs Server"
motd_enabled 0
sv_steamgroup "38347879"
#sv_steamgroup_exclusive 0
sv_minrate 60000
sv_maxrate 0
net_splitpacket_maxrate 60000
sv_hibernate_when_empty 0
EOF

View file

@ -7,6 +7,7 @@
], ],
'bundles': [ 'bundles': [
'wireguard', 'wireguard',
'left4dead2',
], ],
'metadata': { 'metadata': {
'id': 'd5080b1a-b310-48be-bd5a-02cfcecf0c90', 'id': 'd5080b1a-b310-48be-bd5a-02cfcecf0c90',
@ -46,10 +47,11 @@
}, },
}, },
}, },
'nftables': { 'left4dead2': {
'input': { 'servers': {
'tcp dport 27015 accept', 'One': {
'udp dport { 27005, 27015, 27020 } accept', 'port': 27015,
},
}, },
}, },
}, },