AlliedModders

AlliedModders (https://forums.alliedmods.net/index.php)
-   Scripting (https://forums.alliedmods.net/forumdisplay.php?f=107)
-   -   Return arrays in ArrayLists by reference (https://forums.alliedmods.net/showthread.php?t=327820)

Muhlex 10-11-2020 20:02

Return arrays in ArrayLists by reference
 
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.

Ilusion9 10-12-2020 01:19

Re: Return arrays in ArrayLists by reference
 
Use ArrayList.Get to retrieve arraylists from an arraylist. ArrayLists are handles, not arrays.

nosoop 10-12-2020 02:58

Re: Return arrays in ArrayLists by reference
 
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.

Muhlex 10-12-2020 03:32

Re: Return arrays in ArrayLists by reference
 
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.

Peace-Maker 10-12-2020 04:55

Re: Return arrays in ArrayLists by reference
 
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);



Bacardi 10-12-2020 06:53

Re: Return arrays in ArrayLists by reference
 
@Peace-Maker, blyatiful. Thanks for coding example

Muhlex 10-12-2020 08:14

Re: Return arrays in ArrayLists by reference
 
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 (Post 2721051)
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 10-13-2020 07:06

Re: Return arrays in ArrayLists by reference
 
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.

Peace-Maker 10-16-2020 12:12

Re: Return arrays in ArrayLists by reference
 
Quote:

Originally Posted by Muhlex (Post 2721057)
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 (Post 2721172)
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.

Muhlex 10-16-2020 12:40

Re: Return arrays in ArrayLists by reference
 
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.


All times are GMT -4. The time now is 12:35.

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