The Dink Network

Reply to Re: New D-Mod: Cursed Blades Part 1: Charlie's Legacy

If you don't have an account, just leave the password field blank.
Username:
Password:
Subject:
Antispam: Enter Dink Smallwood's last name (surname) below.
Formatting: :) :( ;( :P ;) :D >( : :s :O evil cat blood
Bold font Italic font hyperlink Code tags
Message:
 
 
July 26th 2022, 03:28 PM
goblins.gif
drone1400
Peasant He/Him Romania
C# nerd 
I started looking around the dinkc.cpp source, to see how this argument stuff is working, things are pretty complicated at first glance...

First of all, it seems that all functions, regardless if they're engine functions or user functions, they get processed with the function "get_parms" on line 1748. This function parses a function's parameters and stores them in temporary buffers depending if they are strings or integers (line 50)
/* store current procedure arguments expanded values of type 'int' (see get_parms) */
static long nlist[10];
/* store current procedure arguments of type 'string' (idem) */
static char* slist[10];


Next, the first thing "get_parms" does is to clear these temporary registers, so they are guaranteed to be 0 / empty strings before parsing (line 1750)
  /* Clean-up parameters */
  memset(nlist, 0, 10 * sizeof (int));
  {
    int i = 0;
    for (; i < 10; i++)
      slist[i][0] = '\0';
  }


The function "get_parms" uses the function "decipher" defined on line 603, here you can see that when deciphering the special &return, &arg1 .. &arg8 variables, the &return variable is indeed a GLOBAL one, but the &arg variables are CONTEXT dependent on the current script! (Line 611)
//v1.08 special variables.
  if (dversion >= 108)
    {
      if (compare(variable, "&return"))
	return returnint;
      if (compare(variable, "&arg1"))
	return rinfo[script]->arg1;
      if (compare(variable, "&arg2"))
	return rinfo[script]->arg2;
      if (compare(variable, "&arg3"))
	return rinfo[script]->arg3;
      if (compare(variable, "&arg4"))
	return rinfo[script]->arg4;
      if (compare(variable, "&arg5"))
	return rinfo[script]->arg5;
      if (compare(variable, "&arg6"))
	return rinfo[script]->arg6;
      if (compare(variable, "&arg7"))
	return rinfo[script]->arg7;
      if (compare(variable, "&arg8"))
	return rinfo[script]->arg8;
      if (compare(variable, "&arg9"))
	return rinfo[script]->arg9;
    }


One thing to note is that each function can have up to 10 parameters that can be either string or int. In practice, only engine functions can use string parameters, user functions can only use up to 8 int parameters! Why 8? Because they are called with "external", which passes its own arguments starting with &arg3 to the user function. Parsing the "external" function is done on line 2540. I'm not going to post the whole code segment, but there you can see that "external" parameters are defined like this:
int p[20] = { 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 };

Which means the first two arguments are string, the next 8 are int.

Next, after "external" parses its arguments successfully, it sends them to the called script's own context here: (line 2559)
	      rinfo[myscript1]->arg1 = nlist[2];
	      rinfo[myscript1]->arg2 = nlist[3];
	      rinfo[myscript1]->arg3 = nlist[4];
	      rinfo[myscript1]->arg4 = nlist[5];
	      rinfo[myscript1]->arg5 = nlist[6];
	      rinfo[myscript1]->arg6 = nlist[7];
	      rinfo[myscript1]->arg7 = nlist[8];
	      rinfo[myscript1]->arg8 = nlist[9];


So putting all of this together, it means that the correct behavior for Robj's test code above is like this:

>SCRIPT 1: main gets called, in turn it calls its own test function using "external",
>SCRIPT 2: test gets called, in this context, &arg1 = 1, now external calls its own test2 function
>SCRIPT 3: test2 gets called, in this context, &arg1 = 7, nothing else gets done so the script ends
>SCRIPT 2: we're back in test, it calls "say" next, passing the 1st argument a string with the value of &arg1, and the 2nd argument an int with the value 1, when "get_parms" parses "say" arguments, when "deciphering" the &arg1 value, it should use SCRIPT 2's own context, so say will effectively display "1";

>after say does its thing SCRIPT 2 exits and we are back in SCRIPT 1 which also exits

So I think SlipDink is wrong in his assumption here:
Therefore, in Contact Charlie's example on Discord (reproduced below), we should not expect Dink to say anything but "7".

The only way "say" would evaluate "&arg1" to 7 is if SCRIPT 2 and SCRIPT 3 contexts end up using the same script ID, which should be buggy behaviour...

EDIT: @Robj
This is the way Dink Smallwood has always worked. This is a flaw with the Linux version. I would literally have to take most of my externals and do this for the later ones:
External("<script>", "<proc>", 0, 0, 0, 0, 0, 0, 0, 0);.


This should definitely NOT be necessary ever. According to my understanding of the dinkc parser code, unused arguments should be guaranteed to be 0 when calling a function with "external"...