Page 1 of 1
Stopping workshop maps from overriding cvars
Posted: Tue Apr 28, 2020 12:58 pm
by JustGR
This is my first attempt at writing any kind of plugin, but I need this for the workshop maps that keep overriding my deathmatch config on map load and round start, so here I am. After scouring through the documentation, I attempted to rewrite the cvars affected on round_end and round_poststart events, but the map overrides the cvars even after round_poststart, so this has forced me to reevaluate how I go about this. Is there some way in SourcePython to prevent maps from changing cvars when loaded and triggering rounds?
The cvars in question are mp_roundtime, mp_maxrounds, mp_timelimit, mp_warmuptime and so on. Also I've come across at least one map that forces certain primary weapons on every spawn.
Re: Stopping workshop maps from overriding cvars
Posted: Tue Apr 28, 2020 1:13 pm
by L'In20Cible
Welcome to Source.Python!

You could use the
OnConVarChanged listener to revert the change if the value isn't the one you want to keep. For example:
Syntax: Select all
from listeners import OnConVarChanged
from listeners.tick import Delay
convars = {
'mp_roundtime': '0',
'mp_maxrounds': '0',
'mp_timelimit': '0',
# ...
}
@OnConVarChanged
def on_convar_changed(convar, old_value):
"""Called when a convar has been changed."""
# Get the value we want to keep for the current convar
value = convars.get(convar.name, None)
# If this wasn't a convar we want to preserve the value of, just return
if value is None or convar.get_string() == value:
return
# Revert the change
Delay(0, convar.set_string, (value,))
Re: Stopping workshop maps from overriding cvars
Posted: Tue Apr 28, 2020 4:00 pm
by JustGR
L'In20Cible wrote:Welcome to Source.Python!

You could use the
OnConVarChanged listener to revert the change if the value isn't the one you want to keep. For example:
Syntax: Select all
from listeners import OnConVarChanged
from listeners.tick import Delay
convars = {
'mp_roundtime': '0',
'mp_maxrounds': '0',
'mp_timelimit': '0',
# ...
}
@OnConVarChanged
def on_convar_changed(convar, old_value):
"""Called when a convar has been changed."""
# Get the value we want to keep for the current convar
value = convars.get(convar.name, None)
# If this wasn't a convar we want to preserve the value of, just return
if value is None or convar.get_string() == value:
return
# Revert the change
Delay(0, convar.set_string, (value,))
I already tried @OnConVarChanged, even tried it with a nested Event decorator. This approach works for map loads, but fails when you try to manually change cvars via the console.
Re: Stopping workshop maps from overriding cvars
Posted: Tue Apr 28, 2020 4:49 pm
by decompile
I think using server_cvar event should get manual cvar changing via console.
Syntax: Select all
from events import Event
from cvars import ConVar
from listeners import OnConVarChanged
from listeners.tick import Delay
convars = {
'mp_roundtime': '0',
'mp_maxrounds': '0',
'mp_timelimit': '0',
# ...
}
@Event('server_cvar')
def server_cvar(GameEvent):
"""Called when a convar has been changed."""
cvarname = GameEvent['cvarname'].lower()
cvarvalue = GameEvent['cvarvalue']
# Get the value we want to keep for the current convar
value = convars.get(cvarname , None)
# If this wasn't a convar we want to preserve the value of, just return
if value is None or cvarvalue == value:
return
# Revert the change
Delay(0, ConVar(cvarname).set_string, (value,))
Re: Stopping workshop maps from overriding cvars
Posted: Tue Apr 28, 2020 10:01 pm
by JustGR
decompile wrote:I think using server_cvar event should get manual cvar changing via console.
Syntax: Select all
from events import Event
from cvars import ConVar
from listeners import OnConVarChanged
from listeners.tick import Delay
convars = {
'mp_roundtime': '0',
'mp_maxrounds': '0',
'mp_timelimit': '0',
# ...
}
@Event('server_cvar')
def server_cvar(GameEvent):
"""Called when a convar has been changed."""
cvarname = GameEvent['cvarname'].lower()
cvarvalue = GameEvent['cvarvalue']
# Get the value we want to keep for the current convar
value = convars.get(cvarname , None)
# If this wasn't a convar we want to preserve the value of, just return
if value is None or cvarvalue == value:
return
# Revert the change
Delay(0, ConVar(cvarname).set_string, (value,))
Hey this worked as intended, but since mp_warmup_time does not have a cvars.flags.ConVarFlags.NOTIFY flag, that doesn't get updated. Managed to manually end warmups via @Event('round_announce_warmup'), though it would be nice to just reverse all server cvar changes from the map. Is there a way to give the notify flag to all server cvars edited by the map?
Re: Stopping workshop maps from overriding cvars
Posted: Wed Apr 29, 2020 1:14 am
by L'In20Cible
JustGR wrote:I already tried @OnConVarChanged, even tried it with a nested Event decorator. This approach works for map loads, but fails when you try to manually change cvars via the console.
It works fine. I assume it doesn't because your server is hibernating which consequently means the delay will only process when waking up. You could set
sv_hibernate_when_empty to
0.
Re: Stopping workshop maps from overriding cvars
Posted: Wed Apr 29, 2020 2:11 am
by JustGR
L'In20Cible wrote:JustGR wrote:I already tried @OnConVarChanged, even tried it with a nested Event decorator. This approach works for map loads, but fails when you try to manually change cvars via the console.
It works fine. I assume it doesn't because your server is hibernating which consequently means the delay will only process when waking up. You could set
sv_hibernate_when_empty to
0.
No I meant that it works TOO WELL. It catches all map changes and reverses them to old values, but when I manually change cvars, it goes after those too.
Re: Stopping workshop maps from overriding cvars
Posted: Wed Apr 29, 2020 2:48 am
by L'In20Cible
JustGR wrote:L'In20Cible wrote:JustGR wrote:I already tried @OnConVarChanged, even tried it with a nested Event decorator. This approach works for map loads, but fails when you try to manually change cvars via the console.
It works fine. I assume it doesn't because your server is hibernating which consequently means the delay will only process when waking up. You could set
sv_hibernate_when_empty to
0.
No I meant that it works TOO WELL. It catches all map changes and reverses them to old values, but when I manually change cvars, it goes after those too.
If you want to update the value from the console, you could just use a command. For example:
Syntax: Select all
from commands.server import ServerCommand
from core import console_message
from cvars import ConVar
from listeners import OnConVarChanged
from listeners.tick import Delay
convars = {
'mp_roundtime': '0',
'mp_maxrounds': '0',
'mp_timelimit': '0',
# ...
}
@OnConVarChanged
def on_convar_changed(convar, old_value):
"""Called when a convar has been changed."""
# Get the value we want to keep for the current convar
value = convars.get(convar.name, None)
# If this wasn't a convar we want to preserve the value of, just return
if value is None or convar.get_string() == value:
return
# Revert the change
Delay(0, convar.set_string, (value,))
@ServerCommand('set')
def set_convar(command):
"""Set the value of a ConVar."""
# Was there enough parameters given?
if len(command) < 3:
return console_message('Syntax: set <convar name> <value>\n')
# Extract the parameters
name, value = command[1], command[2]
# Update the convars dictionary
convars[name] = value
# Set the convar value
ConVar(name, value).set_string(value)
Now you could use
set <convar> <value> and it would update the given convar name to that value, etc. Other than that, I can't think of an easy way to determine who initiated the change.
Re: Stopping workshop maps from overriding cvars
Posted: Wed Apr 29, 2020 2:57 am
by JustGR
L'In20Cible wrote:JustGR wrote:L'In20Cible wrote:It works fine. I assume it doesn't because your server is hibernating which consequently means the delay will only process when waking up. You could set sv_hibernate_when_empty to 0.
No I meant that it works TOO WELL. It catches all map changes and reverses them to old values, but when I manually change cvars, it goes after those too.
If you want to update the value from the console, you could just use a command. For example:
Syntax: Select all
from commands.server import ServerCommand
from core import console_message
from cvars import ConVar
from listeners import OnConVarChanged
from listeners.tick import Delay
convars = {
'mp_roundtime': '0',
'mp_maxrounds': '0',
'mp_timelimit': '0',
# ...
}
@OnConVarChanged
def on_convar_changed(convar, old_value):
"""Called when a convar has been changed."""
# Get the value we want to keep for the current convar
value = convars.get(convar.name, None)
# If this wasn't a convar we want to preserve the value of, just return
if value is None or convar.get_string() == value:
return
# Revert the change
Delay(0, convar.set_string, (value,))
@ServerCommand('set')
def set_convar(command):
"""Set the value of a ConVar."""
# Was there enough parameters given?
if len(command) < 3:
return console_message('Syntax: set <convar name> <value>\n')
# Extract the parameters
name, value = command[1], command[2]
# Update the convars dictionary
convars[name] = value
# Set the convar value
ConVar(name, value).set_string(value)
Now you could use
set <convar> <value> and it would update the given convar name to that value, etc. Other than that, I can't think of an easy way to determine who initiated the change.
The server command idea is a good one. Might settle for a typedsaycommand, but we'll see.