English Script to maintain a specific number of players?

11 replies
Goto Page
To the start Previous 1 Next To the start
Up
RichNagel
User
Offline Off
I know nothing about Lua scripting, but saw this script over in the file archive:

http://www.unrealsoftware.de/files_show.php?file=875

That gave me an idea... creating a simple script that would maintain "x" amount of players in the game. Bots would join or leave the game in order to maintain this preset amount of players in the game.

So... after a 'crash course' in Lua scripting <grin> (and browsing the above-linked script), I came up with this:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
players = 10

addhook('ms100','botbalance_ms100')
function botbalance_ms100()
player_count = 0
     for i = 1,32, 1 do
          if (player(i,'exists')) then
               player_count = player_count + 1
          end
     end
     
     if (player_count < players) then
          for i = 1,players, 1 do
               parse('bot_add')
          end
     end
     
     if (player_count > players) then 
     parse('bot_remove')
     end
end


The above will maintain 10 players in the game. If a human joins the game, a bot leaves (and vice versa).


Now, that being said, I thought it would be handy if human spectators were NOT counted for the total amount of players. In other words:

I have 10 bots, five on each team. I join the game as a spectator, and the bots happily play their exisiting game.

If I join a team, one bot on that team is kicked... and if I join the spectators again, a bot will be added to that team.


So... I modified my script like this:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
players = 10

addhook('ms100','botbalance_ms100')
function botbalance_ms100()
player_count = 0
     for i = 1,32, 1 do
          if (player(i,'exists')) and (player(i,'team') ~= 0) then
               player_count = player_count + 1
          end
     end
     
     if (player_count < players) then
          for i = 1,players, 1 do
               parse('bot_add')
          end
     end
     
     if (player_count > players) then 
     parse('bot_remove')
     end
end


...which seems to work perfectly like I described above, but there is one little glitch that I don't care for:

If a human is in the game on a team, and then goes to spectator mode, ALL of the bots are kicked and a fresh new batch of bots are added (to equal out the teams)... thereby RESETTING the round (with the newly added bots).


Is there anything that I could modifiy in the second script so that if there is a human who is on one of the two teams, who then goes into spectator mode, the script will simply add another bot WITHOUT the round restarting?


Thanks in advance for any help
19.12.12 07:25:18 pm
Up
sheeL
User
Offline Off
is only one example of how you start your work

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
addhook("leave","_leave")
addhook("log","_log")

function _log(text)
     if string.sub(text,1,22)=="recv join attempt... (" then
          parse("bot_remove")
     end
end

function _leave(id)
     for i=1,game("sv_maxplayers")-#player(0,"table")+1 do
          timer(100,"parse","bot_add")
     end
end

for i=1,game("sv_maxplayers")-#player(0,"table") do
     parse("bot_add")
end
edited 1×, last 19.12.12 07:46:50 pm
19.12.12 08:57:30 pm
Up
RichNagel
User
Offline Off
Thanks for the help, sheeL Although, that is WAY over my head and understanding <grin>.

That being said, I did come up with this that seems to work:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
players = 10

addhook('ms100','botbalance_ms100')
function botbalance_ms100()
t_player_count = 0
ct_player_count = 0

     for i = 1,32, 1 do
          if (player(i,'exists')) and (player(i,'team') == 1) then
               t_player_count = t_player_count + 1
          end
     end
     
     for i = 1,32, 1 do
          if (player(i,'exists')) and (player(i,'team') == 2) then
               ct_player_count = ct_player_count + 1
          end
     end

     if (t_player_count < players / 2) then
     parse('bot_add_t')
     end

     if (ct_player_count < players / 2) then
     parse('bot_add_ct')
     end
     
     if (t_player_count > players / 2) then 
     parse('bot_remove_t')
     end

     if (ct_player_count > players / 2) then 
     parse('bot_remove_ct')
     end

end


Another question:

Is it possible via Lua to create new CVars that can be entered into the console that the script can proccess? In my script above, it would be really handy to be able to change the "players" variable 'on the fly' using the console (verses having to actually edit the script file).
19.12.12 11:19:49 pm
Up
omg
User
Offline Off
making a new console command would be harder than using a say hook to use lua stuff

basic idea:
Code:
1
2
3
4
5
6
7
addhook("say","bad_function")
function bad_function(id,txt)
     if string.sub(txt,1,7)=="players" then
          players=tonumber(string.sub(txt,9))
          return 1
     end
end

additionally, it may be smart to add a check to see if it's actually you who's using the command
will code for food
20.12.12 12:30:21 am
Up
RichNagel
User
Offline Off
(P[re].S. Scroll to the bottom of this message post)


Thanks for the code and info, omg! I'll try that out ASAP


(*EDIT*)

OK, I've pretty much got this beast working as I wished I ran into a small problem though.

user omg has written:
additionally, it may be smart to add a check to see if it's actually you who's using the command


Here is my current (and pretty much functioning) script:

Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
bot_balance = 5

addhook('join','bot_team_balance_join_server_message')
function bot_team_balance_join_server_message()

     msg("©000255000This server is running Advanced Bot Team Balance v1.0!@C")

end

addhook('say','bot_team_balance_say_commands')
function bot_team_balance_say_commands(player_id,text)

     if (player(1,'exists')) and string.sub(text,1,20)=='bot_team_balance_off' then
          freehook('ms100','bot_team_balance_functions')
          msg("©000255000Bot Team Balance has been disabled.@C")
          msg("©000255000To enable Bot Team Balance, use the command 'bot_team_balance_on'.@C")
          return 1
     end

     if (player(player_id,'rcon') == true) and string.sub(text,1,20)=='bot_team_balance_off' then
          freehook('ms100','bot_team_balance_functions')
          msg("©000255000Bot Team Balance has been disabled.@C")
          msg("©000255000To enable Bot Team Balance, use the command 'bot_team_balance_on'.@C")
          return 1
     end

     if (player(1,'exists')) and string.sub(text,1,19)=='bot_team_balance_on' then
          addhook('ms100','bot_team_balance_functions')
          msg("©000255000Bot Team Balance has been enabled.@C")
          msg("©000255000To disable Bot Team Balance, use the command 'bot_team_balance_off'.@C")
          return 1
     end

     if (player(player_id,'rcon') == true) and string.sub(text,1,19)=='bot_team_balance_on' then
          addhook('ms100','bot_team_balance_functions')
          msg("©000255000Bot Team Balance has been enabled.@C")
          msg("©000255000To disable Bot Team Balance, use the command 'bot_team_balance_off'.@C")
          return 1
     end

     if (player(1,'exists')) and string.sub(text,1,11)=='bot_balance' then
          bot_balance_temp=tonumber(string.sub(text,13))
          bot_balance_temp_text=(string.sub(text,13))
               if (bot_balance_temp ~= nil) then
                    msg("©000255000Bot Team Balance has been changed to "..bot_balance_temp_text.." players per team.@C")
                    else
                    msg("©255000000You have entered an invalid value for the 'bot_balance' command!@C")
                    msg("©000255000Please include the desired number of players per team (e.g. 'bot_balance 5').@C")
               end
          return 1
     end

     if (player(player_id,'rcon') == true) and string.sub(text,1,11)=='bot_balance' then
          bot_balance_temp=tonumber(string.sub(text,13))
          bot_balance_temp_text=(string.sub(text,13))
               if (bot_balance_temp ~= nil) then
                    msg("©000255000Bot Team Balance has been changed to "..bot_balance_temp_text.." players per team.@C")
                    else
                    msg("©255000000You have entered an invalid value for the 'bot_balance' command!@C")
                    msg("©000255000Please include the desired number of players per team (e.g. 'bot_balance 5').@C")
               end
          return 1
     end

end

addhook('ms100','bot_team_balance_functions')
function bot_team_balance_functions()

t_player_count = 0
ct_player_count = 0

     if (bot_balance_temp ~= nil) then
          bot_balance = bot_balance_temp
          else
          bot_balance = bot_balance
     end

     for i = 1,32, 1 do
          if (player(i,'exists')) and (player(i,'team') == 1) then
               t_player_count = t_player_count + 1
          end
     end
     
     for i = 1,32, 1 do
          if (player(i,'exists')) and (player(i,'team') == 2) then
               ct_player_count = ct_player_count + 1
          end
     end

     for i = 1,32, 1 do
          if (player(i,'exists')) and (player(i,'team') == 3) then
               ct_player_count = ct_player_count + 1
          end
     end

     if (t_player_count < bot_balance) then
     parse('bot_add_t')
     end

     if (ct_player_count < bot_balance) then
     parse('bot_add_ct')
     end
     
     if (t_player_count > bot_balance) then
     parse('bot_remove_t')
     end

     if (ct_player_count > bot_balance) then
     parse('bot_remove_ct')
     end

end


In my script above I'm using the "(player(player_id,'rcon') == true)" to see if the player is connected to a dedicated server and is in RCon mode. But, I ALSO wanted this to work for an individual running the script locally on his PC (hosting a "listen" server).

So, I added the "(player(1,'exists'))" sections, as I couldn't RCon when I started a "listen" server. It seemed that every time that I started a "listen" server, I was always player ID "1"... hence the "(player(1,'exists'))" sections.

But, a small snag...

I discovered that when I started a dedicated server, and then started the game and joined the dedicated server (just like any ordinary player would join), occasionally my player ID was "1", even though there were already bots playing (?).

What would you suggest to check if the player is running the script during his own "listen" server game (short of him having to edit the script with something like his own player name or USGN ID)?


(*EDIT #2*)

Looks like it will work properly if I change all of the "say" commands to console commands

e.g.

Code:
1
2
3
4
5
6
7
8
9
10
addhook('parse','bot_team_balance_console_commands')
function bot_team_balance_console_commands(cmd)

     if (cmd=='bot_balance 1') then
          bot_balance = 1
          msg("©000255000Bot Team Balance has been changed to 1 players per team.@C")
          return 1
     end

end


etc...


(*AND YET ANOTHER EDIT*)

The fruit of my labor is here -> http://www.unrealsoftware.de/files_show.php?file=12891 .

Thanks to all of the posters in this thread for all of the help with my FIRST CS2D Lua scripting project!
edited 9×, last 21.12.12 03:22:49 pm
22.12.12 11:16:58 pm
Up
omg
User
Offline Off
well, its one thing to be able to make something work. its more impressive if u can make it work more efficiently

using ms100 is awfully inefficient for something that doesnt need to be monitored precisely; it can easily be changed into a second hook without much difference and saves a lot more function calls

better yet, monitor whenever people leave and join and keep a counter of how many people are in the server that way and then change the ms100 function to a minute function to catch random disconnects. this would be just about the most efficient method possible for monitoring the same thing
will code for food
23.12.12 05:40:48 am
Up
RichNagel
User
Offline Off
user omg has written:
well, its one thing to be able to make something work. its more impressive if u can make it work more efficiently. using ms100 is awfully inefficient for something that doesnt need to be monitored precisely; it can easily be changed into a second hook without much difference and saves a lot more function calls.


Thanks for the heads-up, omg I was thinking the same thing... no real need to do a check 10 times a second.

BTW, here's something that I posted in the comments section of the file I uploaded ( http://www.unrealsoftware.de/files_show.php?file=12891 ), maybe you might be able to give me some ideas how to do this?

-=-=-=-=-=-=-=-=-=-

Folks,

One thing that I've recently discovered is that CS2D flushes all of the variables in a Lua script when the map cycles on a listen or dedicated server. This resets the main team balancing variable to 5 (the default "bot_balance = 5" line included in the Lua script).

Now, being that the 'bot team balance' can be configured via commands (listen server, as well as RCon for a dedicated server) in the console ("bot_balance 6" etc...), it would be SUPER if there was some way to save it to a config file, and then read the variable from the config file after the map cycles (and the Lua script is re-executed).

Anyhow, could anyone give any pointers on how to:

1 - Open a config file: e.g.

Open the filename "AdvancedBotTeamBalance.cfg" (which would be located in the same directory as the "AdvancedBotTeamBalance.lua" script).

2 - Read a line from the config file (in this case, there would be only one line to store the team balance number... BUT, POSSIBLY two lines; a second for if the team balancing routine is enabled or disabled): e.g.

bot_balance = 4
bot_balance_enabled = 1

3 - Read the "4" from the top line, and then pass it on to the Lua script's variable (as well as the "1" and also pass it on as another Lua script variable).

...and ALSO, WRITE to the config file if/when any of the settings have been changed in-game (listen server, or via RCon).

I've read a LOT from this site -> http://www.lua.org/manual/5.1/manual.html , and I THINK it probably would have something to do with using the "io.open", "io.read", "io.write" commands, etc... but it's WAY over my understanding of Lua coding (unless I could see a simple and an easy-to-understand code example).

-=-=-=-=-=-=-=-=-=-
23.12.12 12:11:25 pm
Up
EngiN33R
Moderator
Offline Off
Let's think this through.

You have a script that has to maintain a constant number of players. The number of players only changes if a player joins or leaves. Therefore, we can get rid of the ms100 hook in lieu of a more efficient method, which is join and leave hooks.

About the files - since you store such simple info in your file it really doesn't make much sense to use the I/O library. What you could do instead is simply execute your config file as if it were a Lua script.

Contents of sys/botbalance.cfg (you can change the path):

Code:
1
2
bot_balance = true
bot_balance_pl = 4


Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
pcall(dofile,"sys/botbalance.cfg") -- we execute it as a Lua script, but we do it via pcall just in case that config file doesn't exist

if bot_balance == nil then bot_balance = false end --if no setting is specified default it to disabled

playern = 0

for _,id in pairs(player(0,"table")) do --you should prefer this method to the simple loop of 1 to 32
     playern = playern + 1
end

addhook("join","botbalance_jplayer")
function botbalance_jplayer(id)
     if bot_balance and not player(id,"bot") then
          playern = playern + 1
     end
end

addhook("leave","botbalance_lplayer")
function botbalance_lplayer(id)
     if bot_balance and not player(id,"bot") then
          playern = playern - 1
     end
end

function botbalance()
     if bot_balance then
          local botn=0 --this and the next block is to get the number of bots
          for _,id in pairs(player(0,"table")) do
               if player(id,"bot") then botn=botn+1 end
          end
          local diff = bot_balance_pl-playern-botn --difference that the bots will compensate for
          if diff>0 then
               for i=1,diff do
                    parse("bot_add")
               end
          end
          if diff<=0 then --if there's an excess of players and there's still bots
               for i=1,math.abs(diff) do
                    parse("bot_remove") --remove just the sheer number of bots
               end
          end
end

addhook("join","botbalance")
addhook("leave","botbalance") --add the pair of hooks to the function to make it work

function writeconf()
     local cfg=io.open("sys/botbalance.cfg","w") --open for overwriting
     cfg:write("bot_balance = "..tostring(bot_balance).."\n")
     cfg:write("bot_balance_pl = "..bot_balance_pl)
     cfg:close()
end

-- Now for the config writing part
addhook("parse","schange")
function schange(cmd)
     if cmd:sub(1,3)=="lua" then
          if cmd:find("bot_balance.-%s?=%s?") then --more below
               timer(1,"writeconf") --because this is executed before the command is, we have to wait for the command to take effect and then write the config.
          end
     end
end


To explain the cmd:find part in detail:
More >


I haven't really tested this, but I have a hunch that it will work. Post the errors if they occur and we'll try to help. Also feel free to PM me if you have any questions on Lua scripting in general - I'm always glad to help.
I code, therefore I exist. | Visit my blog for Lua tips and other interesting info
23.12.12 04:23:08 pm
Up
omg
User
Offline Off
i think itd be simpler if bot_balance was saved as an integer

doesnt write have to use +a to keep previous lines? im pretty sure w overwrites previous text

you still need to run the ms100 function occasionally to catch disconnecters that dont "leave"

otherwise, i like the way engineer uses it as another script. saves some io work which (im pretty sure) is more expensive and harder to manage
will code for food
23.12.12 04:57:34 pm
Up
RichNagel
User
Offline Off
Thanks a MILLION for all of the information, EngiN33R (you too, OMG)!

I have just successfully edited the script so that it reads from the config file, and then writes to it with any changes that were issued via the console

I've also added a routine to check if the config file actually exists; and if not, creates a new one with the default parameters (and subsequently executes those defaults).

I'm well on my way with these additions to my meager little LUA project

THANKS AGAIN!
23.12.12 05:41:17 pm
Up
EngiN33R
Moderator
Offline Off
user omg has written:
i think itd be simpler if bot_balance was saved as an integer

doesnt write have to use +a to keep previous lines? im pretty sure w overwrites previous text

you still need to run the ms100 function occasionally to catch disconnecters that dont "leave"

otherwise, i like the way engineer uses it as another script. saves some io work which (im pretty sure) is more expensive and harder to manage


Actually, boolean true/false is less expensive. Numbers in Lua take up 64 bits in memory while boolean values do 8. Basically, boolean values are 8 times as efficient. And you do use a to append, but I need the w mode to overwrite existing settings with the new ones - what's the point of having loads of lines declaring the same variables, but differently?

Yes, you're right about the disconnecters - sometimes players time out without being registered in the leave hook. You have to run the botbalance() function every now and then to compensate for that.

And it's no problem, user RichNagel. Always glad to help.
I code, therefore I exist. | Visit my blog for Lua tips and other interesting info
24.12.12 01:39:41 am
Up
omg
User
Offline Off
i meant save true as 1 and false as 0

oh nvm, forgot it was used like a script
will code for food
To the start Previous 1 Next To the start