Raised This Month: $12 Target: $400
 3% 

Return arrays in ArrayLists by reference


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
Muhlex
Junior Member
Join Date: Mar 2019
Old 10-11-2020 , 20:02   Return arrays in ArrayLists by reference
Reply With Quote #1

Is it possible to retrieve an array pushed into an ArrayList by reference?

As hinted at in the documentation, ArrayList.GetArray() seems to make a copy of the array. Please excuse me if this is a stupid question or if I missed something. Thank you.

Last edited by Muhlex; 10-11-2020 at 20:03.
Muhlex is offline
Ilusion9
Veteran Member
Join Date: Jun 2018
Location: Romania
Old 10-12-2020 , 01:19   Re: Return arrays in ArrayLists by reference
Reply With Quote #2

Use ArrayList.Get to retrieve arraylists from an arraylist. ArrayLists are handles, not arrays.
__________________
Ilusion9 is offline
nosoop
Veteran Member
Join Date: Aug 2014
Old 10-12-2020 , 02:58   Re: Return arrays in ArrayLists by reference
Reply With Quote #3

You can't - the contents of an ArrayList are outside of a plugin's memory space; the only way a plugin is allowed to access it is through the provided natives, and none of them give you that kind of memory access.

ArrayList.Set lets you update specific entries in an array stored in an ArrayList; otherwise you will need to copy data to / from your plugin (as you've already discovered).

What's your particular use case? Without further context this sounds like a preemptive attempt at optimization.
__________________
I do TF2, TF2 servers, and TF2 plugins.
I don't do DMs over Discord -- PM me on the forums regarding inquiries.
AlliedModders Releases / Github / TF2 Server / Donate (BTC / BCH / coffee)

Last edited by nosoop; 10-12-2020 at 03:03.
nosoop is offline
Muhlex
Junior Member
Join Date: Mar 2019
Old 10-12-2020 , 03:32   Re: Return arrays in ArrayLists by reference
Reply With Quote #4

Thanks a lot to you two, that makes total sense.

I'm currently discovering the new enum structs (which is why I'm pushing regular arrays in the ArrayList instead of another ArrayList) and am trying to see what I can get away with by nesting them like a crazy person. Thus for example I have an ArrayList of Players (I realize that isn't as performant as storing a regular old array of these) which looks like this in it's most basic form:

PHP Code:
enum struct Player {
    
int client;
    
int ballEntRef;
    
int ballColor[3];
}

ArrayList players
Now e. g. updating the color value there requires me to players.GetArray(...) (because the enum structs are essentially regular arrays), modify the array/enum and players.SetArray(...) it back to where it was. That works, I just assume it to be rather slow. Doesn't really matter in my case, as I'm just doing setup work with these. Just accessing by reference would have been rather convenient and probably faster as well.
Muhlex is offline
Peace-Maker
SourceMod Plugin Approver
Join Date: Aug 2008
Location: Germany
Old 10-12-2020 , 04:55   Re: Return arrays in ArrayLists by reference
Reply With Quote #5

You can specify which index inside the arraylist entry you get or set using the ArrayList.Get or ArrayList.Set natives ' "block" parameter.

When creating the ArrayList, you set the blocksize to be the number of cells of your enum struct.
PHP Code:
players = new ArrayList(sizeof(Player)); 
Then you can access the different cells/blocks of every pushed enum struct directly by accessing the block at that offset into the enum struct. You can get the cell offset of an enum struct member using the :: operator like Player::ballEntRef. Cells of an array are called "blocks" in ArrayList terms..
PHP Code:
int ballEntRef players.Get(indexPlayer::ballEntRef); 
Your example isn't a great one for this kind of stuff, since there is a known, small fixed upper limit of players which you can just access directly using Player players[MAXPLAYERS+1];
But if we really want to use ArrayLists, you can use them like above. Of course you have to get the correct index into the ArrayList which belongs to the entry for your client first, but luckly ArrayList.FindValue has a block parameter too, so you can look for entries which have the client index at the e.g. 2nd block.

This allows you to access enum struct members without grabbing the whole array - BUT the performance gain you're hoping for is gone once you need to get/set two members at once. Additionally you have to keep a local copy of the enum struct updated yourself.

PHP Code:
enum struct Player {
    
bool cool;
    
int client;
    
int ballEntRef;
    
int ballColor[3];
}

ArrayList players

public 
void OnPluginStart() {
    
players = new ArrayList(sizeof(Player));

    
// Add new entry
    
Player p;
    
p.cool true;
    
p.client 1;
    
p.ballEntRef 1234;
    
p.ballColor = {255,255,255};
    
int index players.PushArray(p);

    
// Change color
    
UpdateColor(1, {123,123,123});

    
// Check color
    
Player p2;
    
players.GetArray(indexp2);
    
PrintToServer("color = {%d, %d, %d}"p2.ballColor[0], p2.ballColor[1], p2.ballColor[2]);
    
// Caution, "p" isn't updated!
    
PrintToServer("old color = {%d, %d, %d}"p.ballColor[0], p.ballColor[1], p.ballColor[2]);

    
int ballEntRef players.Get(indexPlayer::ballEntRef);
    
PrintToServer("ballEntRef = %d"ballEntRef);
}

void UpdateColor(int clientint color[3]) {
    
// Find the Player enum struct entry with that client index.
    
int index players.FindValue(clientPlayer::client);
    if (
index == -1)
        return;

    
// Update the ball color array directly
    
for (int i3i++)
        
players.Set(indexcolor[i], Player::ballColor i);

__________________

Last edited by Peace-Maker; 10-12-2020 at 04:59.
Peace-Maker is offline
Bacardi
Veteran Member
Join Date: Jan 2010
Location: mom's basement
Old 10-12-2020 , 06:53   Re: Return arrays in ArrayLists by reference
Reply With Quote #6

@Peace-Maker, blyatiful. Thanks for coding example
Bacardi is offline
Muhlex
Junior Member
Join Date: Mar 2019
Old 10-12-2020 , 08:14   Re: Return arrays in ArrayLists by reference
Reply With Quote #7

First of, thanks a lot for taking time out of your day to explain this in detail @Peace-Maker. Awesome write-up and great example!

Quote:
Originally Posted by Peace-Maker View Post
Then you can access the different cells/blocks of every pushed enum struct directly by accessing the block at that offset into the enum struct. You can get the cell offset of an enum struct member using the :: operator like Player::ballEntRef. Cells of an array are called "blocks" in ArrayList terms..
PHP Code:
int ballEntRef players.Get(indexPlayer::ballEntRef); 
Wow, I simply didn't know the :: operator existed. Got how ArrayLists worked up until that point and I seem to have done everything, including setting the correct blocksizes, dependant on the enum struct declaration. This definitely helps in using them that way.

One more follow-up question about my example... I'm writing up a basic minigame that (hypothetically) may have multiple instances running simultaneously with different clients on a server belonging to different games. Now I could still use a static Player players[MAXCLIENTS + 1] array per game, however as it is (understandably) not allowed to set multidimensional fields on an enum struct, I could not add the players as a field for e. g. a Game struct.

Is there a workaround that doesn't defeat the purpose of the enum struct syntax, achieving something like this?
PHP Code:
enum struct Player {
  
int client;
  
int someOtherData;
}

enum struct Game {
  
Player players[MAXPLAYERS 1]; // not allowed
  
int winnerOrWhatever;

Muhlex is offline
Muhlex
Junior Member
Join Date: Mar 2019
Old 10-13-2020 , 07:06   Re: Return arrays in ArrayLists by reference
Reply With Quote #8

Another thing... Is it possible to retrieve a method from an enum struct which is stored in an ArrayList and then execute it, without getting a full copy of the enum struct? Similar to how Peace-Maker explained retrieving a field on an enum struct stored inside of an ArrayList.

Somewhat pseudo-code:
PHP Code:
enum struct Thing {
  
int someValue;

  
void ChangeValue() {
    
this.someValue 1234;
  }
}

Function 
MyFunc arrayListOfThings.Get(0Thing::ChangeValue);
Call_StartFunction(INVALID_HANDLEMyFunc); 
Instead of

PHP Code:
Thing myThing;
arrayListOfThings.GetArray(0myThing); // creates a copy
myThing.ChangeValue();
arrayListOfThings.SetArray(0myThing); // need to do this because the method changes the field on the copy only 
Or should I rather just not care / does this approach not make sense, so that I need to stick to the second way of doing it?

EDIT: I just realized that not making a copy of the enum struct before executing it's method would also allow the method to change the enum struct's fields directly. At least that is what I'm hoping for. Updated the examples to reflect what I'm trying to achieve.

Last edited by Muhlex; 10-13-2020 at 07:47.
Muhlex is offline
Peace-Maker
SourceMod Plugin Approver
Join Date: Aug 2008
Location: Germany
Old 10-16-2020 , 12:12   Re: Return arrays in ArrayLists by reference
Reply With Quote #9

Quote:
Originally Posted by Muhlex View Post
One more follow-up question about my example... I'm writing up a basic minigame that (hypothetically) may have multiple instances running simultaneously with different clients on a server belonging to different games. Now I could still use a static Player players[MAXCLIENTS + 1] array per game, however as it is (understandably) not allowed to set multidimensional fields on an enum struct, I could not add the players as a field for e. g. a Game struct.

Is there a workaround that doesn't defeat the purpose of the enum struct syntax, achieving something like this?
PHP Code:
enum struct Player {
  
int client;
  
int someOtherData;
}

enum struct Game {
  
Player players[MAXPLAYERS 1]; // not allowed
  
int winnerOrWhatever;

If the number of players is low like 4 you could just add "player1", "player2", "player3" manually, but that's ugly. You could define a maximum number of simultaneous games and reference another global array.

PHP Code:
#define MAX_GAMES 10

enum struct Player {
  
int client;
  
int someOtherData;
}
Player players[MAX_GAMES][MAXPLAYERS+1];

enum struct Game {
  
int index;
  
int winnerOrWhatever;
  
  
void DoStuff() {
    
this.winnerOrWhatever players[this.index][0].client;
  }
}
Game games[MAX_GAMES];

public 
void thing() {
  
// Find free game slot
  
int index = -1;
  for (
int iMAX_GAMESi++) {
    if (
games[i].index == -1) {
      
index i;
      break;
    }
  }

  
Game game;
  
game.index index;
  
games[index] = game;

I don't think there's a common way for things like this.

Quote:
Originally Posted by Muhlex View Post
Another thing... Is it possible to retrieve a method from an enum struct which is stored in an ArrayList and then execute it, without getting a full copy of the enum struct?
No, to access the fields you have to have a local copy.
__________________
Peace-Maker is offline
Muhlex
Junior Member
Join Date: Mar 2019
Old 10-16-2020 , 12:40   Re: Return arrays in ArrayLists by reference
Reply With Quote #10

Alright, thank you again for the elaborate answer. That about covers all my questions towards the enum struct syntax. It's pretty helpful but doesn't result in great code if overused, as it's just not all that flexible.

Also thinking about it, it is obvious that you can't access the functions via :: as they are not saved in that array. Would be nice to be able to call them from within the plugin, when they are in an ArrayList though.
Muhlex is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 06:29.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode