Author Topic: EfficientPlayersLoop  (Read 2177 times)

0 Members and 1 Guest are viewing this topic.

Offline Savage

  • Soldier
  • **
  • Posts: 155
EfficientPlayersLoop
« on: December 01, 2015, 05:15:04 pm »
Script Name: EfficientPlayersLoop
Script Description: Dynamic array storing active players for maximum loop efficiency.
Original Author(s): Savage
Compilation: Passed
Core Version: 2.8.0 (SC3)

For 2 years I made tons of scripts and I was using 1 to 32 loop to check if player is active(in game). That's not really efficient especially if you are using loop inside AppOnIdle in addition with AppOnIdleTimer set to 1 then the loop is executed 1920 times per second! :o

So there was few solutions:
* Use MaxPlayers property - If server slots are set to 32 we won't achieve anything. If it's set to 16 and there will be player with id 16 and we will change Max_Players amount to 10 (using command /maxplayers or Game.MaxPlayers from SC3) then the loop will be from 1 to 10 and we will miss player with id 16 :)

* Better from above is HighID by ExHunter: http://forums.soldat.pl/index.php?topic=40264.msg494124#msg494124
But if there are 5 players, 4 of them will leave and last guy has id 5 then the loop still is executed 4 useless times. I've noticed few bugs in this topic but the idea isn't bad.

That's all what I knew until i had to write WeaponLimiter(mainly for miniguns) for overloaded VPS.

With that code(written in SC3) the loop is gonna execute that much times how many players are in game:
Code: [Select]
var
psids: array of Byte;
ppos: array[1..32] of Byte;

procedure OnJoin(Player: TActivePlayer; Team: TTeam);
begin
SetLength(psids,high(psids)+2);
psids[high(psids)] := Player.ID;
ppos[Player.ID] := high(psids);
end;

procedure OnLeave(Player: TActivePlayer; Kicked: Boolean);
begin
if psids[high(psids)]=Player.ID then
SetLength(psids,high(psids))
else begin
psids[ppos[Player.ID]] := psids[high(psids)];
ppos[psids[high(psids)]] := ppos[Player.ID];
SetLength(psids,high(psids));
end;
end;

procedure Init;
var
i: Byte;
begin
Game.OnJoin := @OnJoin;
Game.OnLeave := @OnLeave;

for i := 1 to 32 do
if Players[i].Active then begin
SetLength(psids,high(psids)+2);
psids[high(psids)] := i;
ppos[i] := high(psids);
end;
end;

begin
Init;
end.

How it works:
* OnJoin: Players IDs are stored in dynamic array - psids. If the Player joins then psids length is expanded and Player's ID is added to new highest psids's index then Player's position inside psids is saved to ppos array.
* OnLeave: If highest psids index stores Player's ID then psids length is simply shortened by 1 otherwise highest psids's index's Player's ID goes to Player's position who left. Next highest psids's index's Player's ID's ppos takes Player's ppos who left and array is shortened by 1.
* Loop between begin end. : psids array is filled with active players after recompile (works the same as code in OnJoin).

Usage:
* psids index is zero based, example:
Code: [Select]
for i := 0 to high(psids) do
Players[psids[i]].WriteConsole('Hello world!',$FFFFFF);

* If there are no players on the server then psids highest index is -1. Remember to check it in events in which no players can occur (OnLeave, AppOnIdle, OnMapChange, TCP events, OnRequest), example:
Code: [Select]
if high(psids)<>-1 then
for i := 0 to high(psids) do
Players[psids[i]].WriteConsole('Hello world!',$FFFFFF);

Here's my script that's using it: http://forums.soldat.pl/index.php?topic=44076.0
« Last Edit: March 16, 2016, 08:49:54 am by Savage »

DarkCrusade

  • Guest
Re: Efficient Players Loop
« Reply #1 on: December 01, 2015, 05:21:53 pm »
That's a great work-around. :) Good job on that! I bet many scripts would benefit from this addition.

Offline TheOne

  • Soldier
  • **
  • Posts: 208
Re: Efficient Players Loop
« Reply #2 on: December 01, 2015, 08:39:20 pm »
nice idea