Page 1 of 2

Auto Respawn

Posted: Tue Oct 18, 2016 1:53 pm
by Kill
About

Automatically respawn players after their death.


Translation
The plugin supports translation(autorespawn at ../resource/source-python/translations)


Cvars

The plugin will automatically generate a config file (can be found inside the folder ../cfg/source-python)

Code: Select all

autorespawn_enable 1 // Enable the plugin? can either be 1 or 0
autorespawn_delay 2 // Time to wait before spawning a player, in seconds. 0(zero) will instantly respawn the player
autorespawn_showmsg 1 // Show a message to the player before spawning? can either be 1 or 0
autorespawn_unlimited_buytime 1 // Unlimited buy time // can be either 1 or 0

The plugin has a public convar (autorespawn_version).

Installation
Extract the zip
Drag and drop
In server or autoexec(or manually using the server console)

Code: Select all

sp plugin load autorespawn


Thanks to
  • Ayuto
  • iPlayer
  • L'In20Cible.
  • satoon101

Download
autorespawn.zip
Updated
(2.74 KiB) Downloaded 454 times

Re: Auto Respawn

Posted: Tue Oct 18, 2016 2:34 pm
by L'In20Cible
Cool plugin!

Here are few points:


  • Instead of having a function that call the spawn method on the passed player, you could simply delay that method to be called:

    Syntax: Select all

    Delay(cvars['autorespawn_delay'].get_int(), player.spawn)

    However, I would not recommend delaying any Player/Entity's method calls and/or passing them as argument. If the player leave the server/entity get removed before the call is made, you gonna either crash or your code will raise an error. In this specific case, I would rather recommend passing the index and recast the Player instance:

    Syntax: Select all

    # Into player_death...
    Delay(cvars['autorespawn_delay'].get_int(), respawn_player, player.index)

    def respawn_player(index):
    try:
    player = Player(index)
    except ValueError:
    # Player left the server, nothing to do here...
    return
    player.spawn()


    Another thing you should consider, is to store the Delay instances and cancel them on new round/map.

  • Like I mentioned here about TempEntity instances, I would recommend the same thing for messages. In your plugin, you could globalize the SayText2 instance and simply send it to the players instead of recreating the instance every time. Also, you should update the respawn_delay when the ConVar value is updated, otherwise time won't match if it is changed after the plugin has been loaded.

  • Instead of modifying the settings of the server by changing the value of mp_buytime, you could simply use a hook to allow players to buy at any time:

    Syntax: Select all

    from entities.entity import BaseEntity
    from entities.hooks import EntityCondition
    from entities.hooks import EntityPreHook
    from memory import make_object

    @EntityPreHook(EntityCondition.is_player, 'post_think')
    def pre_post_think(stack_data):
    if not (cvars['autorespawn_enable'].get_bool() and
    cvars['autorespawn_unlimited_buytime'].get_bool()):
    return
    entity = make_object(BaseEntity, stack_data[0])
    entity.set_network_property_bool('m_bInBuyZone', True)
    You could also add support so players could only buy for a specific amount of time after respawning or something like that.

Re: Auto Respawn

Posted: Tue Oct 18, 2016 3:09 pm
by satoon101
L'In20Cible wrote:
    However, I would not recommend delaying any Player/Entity's method calls and/or passing them as argument. If the player leave the server/entity get removed before the call is made, you gonna either crash or your code will raise an error. In this specific case, I would rather recommend passing the index and recast the Player instance:

I think I would rather pass in the player's userid. Another player could easily take over the disconnected player's index, especially with a higher respawn delay. Or at least store the Delay instances in a dictionary by the player's userid and cancel/remove the Delay when the player disconnects.


L'In20Cible wrote:
  • Like I mentioned here about TempEntity instances, I would recommend the same thing for messages. In your plugin, you could globalize the SayText2 instance and simply send it to the players instead of recreating the instance every time. Also, you should update the respawn_delay when the ConVar value is updated, otherwise time won't match if it is changed after the plugin has been loaded.

As an example:

Syntax: Select all

RESPAWN_MSG = SayText2(
message="{prefix} You'll be respawned in {respawn_delay} seconds"
)


...
delay_time = cvars['autorespawn_delay'].get_int()
if cvars['autorespawn_showmsg'].get_int():
RESPAWN_MSG.send(
player.index,
prefix=CHAT_PREFIX,
respawn_delay=delay_time
)
Delay(delay_time, respawn_player, player.userid)

Re: Auto Respawn

Posted: Tue Oct 18, 2016 3:14 pm
by iPlayer
I'd also like to add that you should probably check if player is dead right before respawning them. Because if the game has already respawned them for any purpose (I don't know why - round restart, team swap, another plugin) and you perform player.spawn() on them again, the player will get teleported to a new spawnpoint. Now, depending on your respawn delay, they could've been already fighting somebody, and teleporting back to spawn is a bit frustrating.

And I agree with Satoon. I'd rather store the Delay objects and cancel them when player disconnects. That way you can still safely pass Player instance to them.

Re: Auto Respawn

Posted: Tue Oct 18, 2016 3:38 pm
by Kill
Thanks for the info guys!

L'In20Cible wrote:Cool plugin!

Here are few points:


  • Instead of having a function that call the spawn method on the passed player, you could simply delay that method to be called:

    Syntax: Select all

    Delay(cvars['autorespawn_delay'].get_int(), player.spawn)

    However, I would not recommend delaying any Player/Entity's method calls and/or passing them as argument. If the player leave the server/entity get removed before the call is made, you gonna either crash or your code will raise an error. In this specific case, I would rather recommend passing the index and recast the Player instance:

    Syntax: Select all

    # Into player_death...
    Delay(cvars['autorespawn_delay'].get_int(), respawn_player, player.index)

    def respawn_player(index):
    try:
    player = Player(index)
    except ValueError:
    # Player left the server, nothing to do here...
    return
    player.spawn()


    Another thing you should consider, is to store the Delay instances and cancel them on new round/map.

Example please? I don't quite understand that, or how to do so.

L'In20Cible wrote:
  • Like I mentioned here about TempEntity instances, I would recommend the same thing for messages. In your plugin, you could globalize the SayText2 instance and simply send it to the players instead of recreating the instance every time. Also, you should update the respawn_delay when the ConVar value is updated, otherwise time won't match if it is changed after the plugin has been loaded.


Watch the Convar change, I'll add that, but usually, I'll simply reload the plugin, don't You think?

L'In20Cible wrote:
  • Instead of modifying the settings of the server by changing the value of mp_buytime, you could simply use a hook to allow players to buy at any time:

    Syntax: Select all

    from entities.entity import BaseEntity
    from entities.hooks import EntityCondition
    from entities.hooks import EntityPreHook
    from memory import make_object

    @EntityPreHook(EntityCondition.is_player, 'post_think')
    def pre_post_think(stack_data):
    if not (cvars['autorespawn_enable'].get_bool() and
    cvars['autorespawn_unlimited_buytime'].get_bool()):
    return
    entity = make_object(BaseEntity, stack_data[0])
    entity.set_network_property_bool('m_bInBuyZone', True)
    You could also add support so players could only buy for a specific amount of time after respawning or something like that.


Thanks, but for the time being, I'll manipulate the buytime, as I don't know/have no idea on how to use the memory module.

Re: Auto Respawn

Posted: Tue Oct 18, 2016 5:29 pm
by Ayuto
Kill wrote:
L'In20Cible wrote:
  • Like I mentioned here about TempEntity instances, I would recommend the same thing for messages. In your plugin, you could globalize the SayText2 instance and simply send it to the players instead of recreating the instance every time. Also, you should update the respawn_delay when the ConVar value is updated, otherwise time won't match if it is changed after the plugin has been loaded.


Watch the Convar change, I'll add that, but usually, I'll simply reload the plugin, don't You think?

L'In20Cible is talking about this piece:

Syntax: Select all

RESPAWN_MSG = "{prefix} You'll be respawned in {respawn_delay} seconds".format(
prefix=CHAT_PREFIX,
respawn_delay=cvars['autorespawn_delay'].get_int()
)
It's only evaluated once when the plugin loads, so the message will never get updated when you change the autorespawn_delay cvar. However, Satoon's solution also solves this problem.

Btw. you might want to change the prefix to something else. "[SP]" is kinda misleading.

And one last point: I'm not a big fan of cvars like "autorespawn_enable". Back in ES I saw a lot people doing that, but there is actually no need to do that. If you want to disable a plugin, you can simply unload it. This only introduces complexity and your code is growing for no reason.

Re: Auto Respawn

Posted: Wed Oct 19, 2016 2:10 pm
by Kill
Ayuto wrote:
Kill wrote:
L'In20Cible wrote:...


Thanks :)

Updated the plugin, added player check, translation, changed SayText2 and added a ConVar listener.

Re: Auto Respawn

Posted: Wed Oct 19, 2016 9:13 pm
by Ayuto
I guess you got me wrong. You don't need the convar listener. Just remove the convar listener part.

Re: Auto Respawn

Posted: Thu Oct 20, 2016 2:55 am
by satoon101
Yeah, the reason that tracking the ConVar change was mentioned was to update the SayText2 message value when it was. However, the solution you went with, from my example, eliminates the need for that, as it gets the value at that exact moment when you send the message. Currently, your OnConVarChanged function doesn't actually do anything.

Also, I completely agree with this:
Ayuto wrote:And one last point: I'm not a big fan of cvars like "autorespawn_enable". Back in ES I saw a lot people doing that, but there is actually no need to do that. If you want to disable a plugin, you can simply unload it. This only introduces complexity and your code is growing for no reason.

There is no need for an enable/disable feature for the entire plugin. If someone wants to disable it, all they have to do is use:

Code: Select all

sp plugin unload <plugin>

Re: Auto Respawn

Posted: Thu Oct 20, 2016 10:41 am
by decompile
Those _enable cvars are mostly used through mapconfig' plugins, so instead of rather unloading the complete plugin, you just enable/disable the cvar on specific maps, but in the end its probably just as sp plugin unload.

Re: Auto Respawn

Posted: Sun Oct 23, 2016 11:20 am
by Kill
Ayuto wrote:I guess you got me wrong. You don't need the convar listener. Just remove the convar listener part.


Can You please show me some code to help me understand? Thanks!

Re: Auto Respawn

Posted: Sun Oct 23, 2016 12:06 pm
by satoon101
There is no code to show you. Again:
satoon101 wrote:Yeah, the reason that tracking the ConVar change was mentioned was to update the SayText2 message value when it was. However, the solution you went with, from my example, eliminates the need for that, as it gets the value at that exact moment when you send the message. Currently, your OnConVarChanged function doesn't actually do anything.

Re: Auto Respawn

Posted: Sun Oct 23, 2016 12:07 pm
by decompile
satoon101 wrote:There is no code to show you. Again:
satoon101 wrote:Yeah, the reason that tracking the ConVar change was mentioned was to update the SayText2 message value when it was. However, the solution you went with, from my example, eliminates the need for that, as it gets the value at that exact moment when you send the message. Currently, your OnConVarChanged function doesn't actually do anything.


So when Trying to get the config value, it will auto update when it gets changed?
Or did I understood it wrong?

I remember atleast that what he did, every sm plugin needs to do themselves.

Re: Auto Respawn

Posted: Sun Oct 23, 2016 12:25 pm
by iPlayer
decompile wrote:
satoon101 wrote:There is no code to show you. Again:
satoon101 wrote:Yeah, the reason that tracking the ConVar change was mentioned was to update the SayText2 message value when it was. However, the solution you went with, from my example, eliminates the need for that, as it gets the value at that exact moment when you send the message. Currently, your OnConVarChanged function doesn't actually do anything.


So when Trying to get the config value, it will auto update when it gets changed?
Or did I understood it wrong?

I remember atleast that what he did, every sm plugin needs to do themselves.


There're 2 or more approaches to go in Kill's plugin.

1.
Make SayText2 instance static and register OnConVarChanged listener, so that when the convar updates, the SayText2 instance is rebuilt.

2.
Rebuild SayText2 every time, and request convar "on demand". No listeners needed.

That's I believe where the confusion comes from. The problem is actually solved by creating static SayText2 instance, but sending it with the up-to-date arguments.

Re: Auto Respawn

Posted: Sun Oct 23, 2016 1:55 pm
by satoon101
decompile wrote:So when Trying to get the config value, it will auto update when it gets changed?
Or did I understood it wrong?

In the plugin, where you send the message, you pass in the 'current' value of the ConVar in the **tokens:

Syntax: Select all

RESPAWN_MSG.send(
player.index,
prefix=CHAT_PREFIX,
respawn_delay=cvars['autorespawn_delay'].get_int()
)

So, when you pass in respawn_delay=cvars['autorespawn_delay'].get_int() , you are passing in the value of the ConVar at that exact moment.

Also, as far as your OnConVarChanged function:

Syntax: Select all

@OnConVarChanged
def on_convar_changed(convar, old_val):
if convar.name == info.basename + "_delay" and convar.get_int() != int(old_val):
from core import console_message
console_message("\n----------------------------------\n"
"[SP] ConVar change: {}, old value: {}, new value: {}\n"
"-----------------------------------\n".format(
convar.name,
old_val,
convar.get_int()
))
cvars['autorespawn_delay'] = convar

cvars['autorespawn_delay'] is already set to that ConVar. You're basically setting it to itself again. So, that function does absolutely nothing. But again, this function is completely unnecessary, because you are already passing the correct value in as the respawn_delay token.

Re: Auto Respawn

Posted: Sun Oct 23, 2016 1:57 pm
by satoon101
iPlayer wrote:The problem is actually solved by creating static SayText2 instance, but sending it with the up-to-date arguments.

That's exactly what the plugin already does.

Re: Auto Respawn

Posted: Fri Nov 04, 2016 6:54 pm
by Kill
Updated.
Removed the ConVar listener.

Re: Auto Respawn

Posted: Sat Nov 05, 2016 3:26 pm
by Ayuto
Hey, now you are retrieving the value of those four convars once at the beginning, so a change of them has no effect in your plugin:

Syntax: Select all

ar_enabled = cvars['autorespawn_enable'].get_bool()
ar_delay = cvars['autorespawn_delay'].get_int()
ar_showmsg = cvars['autorespawn_showmsg'].get_bool()
ar_buytime = cvars['autorespawn_unlimited_buytime'].get_bool()
You need to do it like this:

Syntax: Select all

ar_enabled = cvars['autorespawn_enable']
ar_delay = cvars['autorespawn_delay']
ar_showmsg = cvars['autorespawn_showmsg']
ar_buytime = cvars['autorespawn_unlimited_buytime']

@Event("player_death")
def event_player_death(game_event):
if ar_enabled.get_bool():
player = Player.from_userid(game_event.get_int("userid"))
if ar_showmsg.get_bool():
RESPAWN_MSG.send(
player.index,
prefix=CHAT_PREFIX,
respawn_delay=ar_delay.get_int()
)
Delay(ar_delay.get_int(), respawn, player.userid)

Re: Auto Respawn

Posted: Sat Nov 05, 2016 3:42 pm
by Kill
Ayuto wrote:...

Aaaah, Thanks!
Updated!

Re: Auto Respawn

Posted: Sat Nov 05, 2016 4:04 pm
by L'In20Cible
Also, the following:

Syntax: Select all

Delay(ar_delay.get_int(), respawn, player.userid)

def respawn(userid):
try:
player = Player.from_userid(userid)
except ValueError:
return
if player.dead: # is player dead?
player.spawn()

Could now simply be:

Syntax: Select all

player.delay(ar_delay.get_int(), player.spawn)