Strange FindPlayer, Player.Cash issue

Started by Rhytz, Apr 14, 2017, 01:09 PM

Previous topic - Next topic

Rhytz

I'm actually having this issue on LU, but I was advised to ask here since we couldn't figure it out.

So I'm creating this minigame script, and at the end of each minigame, every participating player gets paid a certain amount of money depending on how well they played (0.8x of what the previous player got).

Edit, pastebin'd for better readabilty: https://pastebin.com/y0Rn3Fnm
function payoutPlayers(){
local topArray = [];
foreach(id, score in playerScores){
if(score > 0){
topArray.push({id = id, score = score});
}
}

topArray.sort(sortScores);
local _payout = currentMinigame.payout;
foreach(pos, scoreinfo in topArray){
local _player = FindPlayer(scoreinfo.id);
if(_player){
local realpos = pos + 1;
//_player.Cash += _payout;
BigMessage(_player, "You came " + realpos + " in the " + currentMinigame.name, 5000, 3);
_payout = abs(_payout * 0.8);
}
_player = null;
}
}

So this code works, until you uncomment the _player.Cash += _payout; line. Then only the first iteration will succeed, and it will error on the second one. For some reason when that line is uncommented, _player is set to the player ID(int), instead of a Player reference.

Here's testing with that line commented out, and some debug prints. All works fine:
SCRIPT: before if(_player): Theremin
SCRIPT: before _player.Cash: Theremin
SCRIPT: Cash: 18005 _player: Theremin(Player)pos: 1 payout: 15000(integer)
SCRIPT: before BigMessage: Theremin
SCRIPT: Cash: 18005 _player: Theremin(Player)pos: 1 payout: 15000(integer)
SCRIPT: before if(_player): Rhytz
SCRIPT: before _player.Cash: Rhytz
SCRIPT: Cash: 4054 _player: Rhytz(Player)pos: 2 payout: 12000(integer)
SCRIPT: before BigMessage: Rhytz
SCRIPT: Cash: 4054 _player: Rhytz(Player)pos: 2 payout: 12000(integer)
SCRIPT: [lu-minigames] Minigame ended

However, when I uncomment the _player.Cash += _payout; line, this is what I get:
SCRIPT: before if(_player): Theremin
SCRIPT: before _player.Cash: Theremin
SCRIPT: Cash: 18005 _player: Theremin(Player)pos: 1 payout: 15000(integer)
SCRIPT: before BigMessage: Theremin
SCRIPT: Cash: 33005 _player: Theremin(Player)pos: 1 payout: 15000(integer)
SCRIPT: before if(_player): 1
SCRIPT: before _player.Cash: 1

AN ERROR HAS OCCURED [the index 'Cash' does not exist]

CALLSTACK
*FUNCTION [payoutPlayers()] Scripts/lu-minigames/main_s.nut line [164]
*FUNCTION [stopMinigame()] Scripts/lu-minigames/main_s.nut line [141]

LOCALS
[playerScores] ARRAY
[currentMinigame] TABLE
[realpos] 2
[_player] 1
[@ITERATOR@] 2
[scoreinfo] TABLE
[pos] 1
[_payout] 12000
[topArray] ARRAY
[this] TABLE
[minigameState] "ended"
[eligiblePlayers] TABLE
[currentMinigame] TABLE
[this] TABLE

So can anyone tell if there is something wrong with my code? Or have a run into an unfortunate LU bug?

Hoping someone can help

KAKAN

AN ERROR HAS OCCURED [the index 'Cash' does not exist]

CALLSTACK
*FUNCTION [payoutPlayers()] Scripts/lu-minigames/main_s.nut line [164]
*FUNCTION [stopMinigame()] Scripts/lu-minigames/main_s.nut line [141]

LOCALS
[playerScores] ARRAY
[currentMinigame] TABLE
[realpos] 2
[_player] 1
[@ITERATOR@] 2
[scoreinfo] TABLE
[pos] 1
[_payout] 12000
[topArray] ARRAY
[this] TABLE
[minigameState] "ended"
[eligiblePlayers] TABLE
[currentMinigame] TABLE
[this] TABLE
See the LOCALS section.
_player is 1, it should be a instance instead.
So, try removing the _player = null; from your code and give it a try. I know this is a really stupid thing to do. But, you know, VCMP and LU( Squirrel too, sometimes ) give unexpected results :)
And, try this one as well, dunno for sure, but might do the trick :)
https://pastebin.com/uqQ1agtVI've ran into similar problem and that solved the thing, still dunno how.
oh no

Rhytz

#2
I added _player = null; in an attempt to fix the issue, but it didn't help unfortunately. I think FindPlayer must be returning that 1, after _player.Cash is called in the previous iteration.

I have no clue where else that 1 could come from?

Again, the code works fine without the _player.Cash += _payout; line... Then the _player variable contains a valid Player instance in every iteration, and every player sees the BigMessage. With that line, only the first player gets to see the BigMessage, AND gets the money, but the loop errors and halts on the second iteration.

Edit: didnt see your pastebin fix, gonna test, ty!

Rhytz

#3
Unfortunately the problem persisted using that code, and whatever i tried. So I'm now resorting to a somewhat gross workaround, but it does the job.

NewTimer("delayedCash", realpos * 100, 1, _player, _payout);

function delayedCash(player, cash){
player.Cash += cash;
}

So here's the finished product:
function payoutPlayers(){
local topArray = [];
foreach(id, score in playerScores){
if(score > 0){
topArray.push({id = id, score = score});
}
}

topArray.sort(sortScores);
local _payout = currentMinigame.payout;
foreach(pos, scoreinfo in topArray){
local _player = FindPlayer(scoreinfo.id);
if(typeof(_player) != "Player") _player = FindPlayer( scoreinfo.id );

if(_player && typeof(_player) == "Player"){ 
local realpos = pos + 1;
//_player.Cash += _payout; //Why does this not work!!!!??!?!?
//Delay the payout, this does seem to work
NewTimer("delayedCash", realpos * 100, 1, _player, _payout);
BigMessage(_player, "You came " + realpos + " in the " + currentMinigame.name, 5000, 3);
_payout = abs(_payout * 0.8);
}else{
print("Error retrieving player for payout");
}
}
}

function delayedCash(player, cash){
player.Cash += cash;
}

If anyone has a more solid solution than what I did, I'm all ears  :)

kennedyarz

Quote from: Rhytz on Apr 14, 2017, 06:19 PMif(typeof(_player) != "Player") _player = FindPlayer( scoreinfo.id );
      if(_player && typeof(_player) == "Player")      

Hi, I do not know anything about LU, but you could not deal with those capital letters?

EK.IceFlake

A much more solid workaround would be to keep an array of every online player with their instance:
players <- {};
function LUEventForPlayerJoin(player)
{
    players.rawset(player.ID, player);
}
function LUEventForPlayerLeave(player)
{
    players.rawdelete(player.ID);
}
function MegaFindPlayer(playerid)
{
    if (players.rawin(playerid)) return players[playerid];
    else return null;
}

KAKAN

Quote from: Rhytz on Apr 14, 2017, 06:19 PMUnfortunately the problem persisted using that code, and whatever i tried. So I'm now resorting to a somewhat gross workaround, but it does the job.

NewTimer("delayedCash", realpos * 100, 1, _player, _payout);

function delayedCash(player, cash){
player.Cash += cash;
}

So here's the finished product:
function payoutPlayers(){
local topArray = [];
foreach(id, score in playerScores){
if(score > 0){
topArray.push({id = id, score = score});
}
}

topArray.sort(sortScores);
local _payout = currentMinigame.payout;
foreach(pos, scoreinfo in topArray){
local _player = FindPlayer(scoreinfo.id);
if(typeof(_player) != "Player") _player = FindPlayer( scoreinfo.id );

if(_player && typeof(_player) == "Player"){ 
local realpos = pos + 1;
//_player.Cash += _payout; //Why does this not work!!!!??!?!?
//Delay the payout, this does seem to work
NewTimer("delayedCash", realpos * 100, 1, _player, _payout);
BigMessage(_player, "You came " + realpos + " in the " + currentMinigame.name, 5000, 3);
_payout = abs(_payout * 0.8);
}else{
print("Error retrieving player for payout");
}
}
}

function delayedCash(player, cash){
player.Cash += cash;
}

If anyone has a more solid solution than what I did, I'm all ears  :)
an idea: Use findplayer again to set player's cash. Something like:
FindPlayer( whatever).Cash
It should work as we already know that the player exists haha
oh no

vito

He is using "_player" instance in timers...
NewTimer("delayedCash", realpos * 100, 1, _player, _payout);

KAKAN

Quote from: vito on Apr 15, 2017, 08:11 AMHe is using "_player" instance in timers...
NewTimer("delayedCash", realpos * 100, 1, _player, _payout);
LU works with that. See the first code. That's the thing.
oh no

Rhytz

Quote from: EK.IceFlake on Apr 15, 2017, 04:05 AMA much more solid workaround would be to keep an array of every online player with their instance

Good stuff, woke up today with my mind all cleared up and a similar idea... Why am I not simply storing the player instance in the array, so I don't need FindPlayer(). Gonna try that, thanks!

Quote from: kennedyarz on Apr 15, 2017, 12:26 AMHi, I do not know anything about LU, but you could not deal with those capital letters?

typeof(_player) returns "Player"

Quote from: KAKAN on Apr 15, 2017, 09:17 AMLU works with that. See the first code. That's the thing.

Yeah i prefixed the variables with an underscore just in case something else in the script was interfering with those variables (i use player and payout variables elsewhere)

Anyway thanks for the help guys, gonna poke around with it and see if I can create a better solution. Will report back in!

KAKAN

#10
Quote from: Rhytz on Apr 15, 2017, 10:47 AMAnyway thanks for the help guys, gonna poke around with it and see if I can create a better solution. Will report back in!
um, you forgot this?
Quote from: KAKAN on Apr 15, 2017, 07:34 AMan idea: Use findplayer again to set player's cash. Something like:
FindPlayer( whatever).Cash
It should work as we already know that the player exists haha
Here's the code:
function payoutPlayers(){
local topArray = [];
foreach(id, score in playerScores){
if(score > 0){
topArray.push({id = id, score = score});
}
}

topArray.sort(sortScores);
local _payout = currentMinigame.payout;
foreach(pos, scoreinfo in topArray){
local _player = FindPlayer(scoreinfo.id);
if(_player && typeof _player == "Player"){
local realpos = pos + 1;
FindPlayer(scoreinfo.id).Cash += _payout;
BigMessage(_player, "You came " + realpos + " in the " + currentMinigame.name, 5000, 3);
_payout = abs(_payout * 0.8);
}
else
print("Nope.");
}
}
oh no

Rhytz

Quote from: KAKAN on Apr 15, 2017, 11:24 AMum, you forgot this?

I refactored the code so I wouldn't have to rely on FindPlayer and the player ID to get the player instance. Now when a player scores his first point, his player instance is added to the playerScores array. Now it all works fine, without the ugly timer, and without FindPlayer :)

Here's the final code:
function payoutPlayers(){
local topArray = [];
foreach(scoreinfo in playerScores){
if(scoreinfo){
topArray.push({player = scoreinfo.player, score = scoreinfo.score});
}
}

topArray.sort(sortScores);
local _payout = currentMinigame.payout;
foreach(pos, scoreinfo in topArray){
if(scoreinfo.player){ 
local realpos = GetNth(pos + 1);
scoreinfo.player.Cash += _payout;
BigMessage(scoreinfo.player, "You came " + realpos + " in " + currentMinigame.name, 5000, 3);
_payout = abs(_payout * 0.8);
}
}
}

Here's a little preview of the script in action:
http://www.youtube.com/watch?v=6N9BtZgMCS0#

EK.IceFlake

Kinda a bump but
Quote from: Programming conventionsAnd thy code indentify

Rhytz

If by that you mean indent it properly... For some reason the [ code ] tag gets rid of my indentation. It still looks right in the preview though