I'm considering getting into creating a custom map in SC2, but I was wondering, are there are any current extensions of Galaxy?
By extension I mean adding scripting capability, e.g. un-requiring forward declarations, adding struct inheritance, for loops, array resizing, "++" and "--" operators, classes (essentially structs with more functionality), block commenting, etc.
Back in WC3, there was vJASS, an extension of JASS. Looking around, there used to be Andromeda and Galaxy++, but they seem to be discontinued. Has anything taken their place, or are we back to just vanilla Galaxy?
By extension I mean adding scripting capability, e.g. un-requiring forward declarations, adding struct inheritance, for loops, array resizing, "" and "" operators, classes (essentially structs with more functionality), block commenting, etc.
That is not possible due to how the Galaxy virtual machine works. The language is inherently very safe, to the point it is pretty much impossible to do many standard programming practices (no dynamic memory allocation, no pointers, no raw memory manipulation etc).
A pre-compiler could be made to do some of it like object orientated inheritance and block comments however you still will need to statically allocate structures. Another big issue is that being unable to return or store struct and array references makes it very difficult to link objects together without linking using a static function. Many existing extensions have got around this using the data table functionality to provide dynamic memory but this drops performance closer to interpreted languages so is not recommended for performance critical tasks.
As such it generally is better to program in Embedded C style rather than try and do anything object orientated. It can be a pain for some highly dynamic systems and code reusability but in the end you want something that works.
Quote:
Back in WC3, there was vJASS, an extension of JASS. Looking around, there used to be Andromeda and Galaxy, but they seem to be discontinued. Has anything taken their place, or are we back to just vanilla Galaxy?
WC3's JASS was an interpreted language so did not have the issue of static linking like Galaxy does. It also had no pre-defined memory space meaning that you could allocate excessive memory to solve problems like dynamic allocation by using the dynamic array functionality of JASS arrays. However all this came at the cost of execution being extremely slow to the point where complex systems often ran into performance issues rather than implementation issues.
A major improvement with Galaxy is that it actually has a reasonable GUI front. You can perform most tasks using GUI and get triggers which perform similar to pure hand written Galaxy. In fact it is recommended to use GUI for most things as the editor supports it better (type safety etc). I only recommend writing Galaxy for very specialist purposes such as bit manipulation (unsupported by GUI), reference support (unsupported by GUI) and some AI natives (for some reason unsupported by GUI). This is in stark contrast with WC3's GUI where the resulting code was such a mess and disaster you had to use JASS heavily unless you wanted your map to be a poor performing pile of leaks.
You can also mix Galaxy with GUI pretty well in SC2E. Arguments can be derived from Galaxy statements so you can often get away with just a single piece of Galaxy for the functionality you want.
What ImperialGood has stated is true, however, as an addendum, the galaxy VM and scripting language is being upgraded and some of the limitations he has listed will no longer apply when we get Legacy of the Void. Specifically, you will be able to pass, store and return struct, function and array references.
I agree with everything that was said, plus I personally would never recommend using "inofficial" extentions to the language because almost all of them will be outdated and unsupported at one point. You may also run into bugs and performance issues that at the end of the day may cost you more time than the new language features can save.
Do not work against the language, instead, try developing practises which are using it to its full potential.
Quote:
Many existing extensions have got around this using the data table functionality to provide dynamic memory but this drops performance closer to interpreted languages so is not recommended for performance critical tasks.
I'm a little bit confused by this statement. To my knowledge, Galaxy is an interpreted language, so it cannot really get "closer to interpreted performance". In my own benchmarks mathematical calculations were about 400x slower than in C, and there seems to be no compilation of the code, just syntax checking.
Quote:
What ImperialGood has stated is true, however, as an addendum, the galaxy VM and scripting language is being upgraded and some of the limitations he has listed will no longer apply when we get Legacy of the Void. Specifically, you will be able to pass, store and return struct, function and array references.
Interresting, can you provide more information? By "Store" do you mean there is some sort of new type that allows storing references in data tables? Passing and storing references in form of variables is already possible.
EDIT: Okay so i made some tests, and all this appears to already work in the current build of Starcraft.
I'm a little bit confused by this statement. To my knowledge, Galaxy is an interpreted language, so it cannot really get "closer to interpreted performance". In my own benchmarks mathematical calculations were about 400x slower than in C, and there seems to be no compilation of the code, just syntax checking.
It all depends on what you define as an "interpreted" language. Galaxy is interpreted in the sense that it does not run native machine code however is compiled in the sense that it runs virtual machine code. It has no pre-compile for code during save but does compile code during map load into virtual machine code. After the code is compiled everything becomes statically linked based on the virtual machine code assembly behaviour (why there are memory limits and things like that). A function name table must still be kept for dynamic resolution for triggers but that probably only resolves a function reference once during trigger creation. The result is a language that is pretty fast (comparatively). It could be made even faster by going down the Java route of JIT compiling the virtual machine code into native machine code and optimizing for near native performance but I have doubts that Blizzard is willing to put that much effort into the language. That said, looking at Heroes of the Storm it might be that pre-compiled Galaxy code (compiled on save) will be coming in LotV as there were a lot of strange files mixed with the script files.
This is in contrast to WC3 and JASS. Although it did use some kind of virtual machine for basic line execution it performed all named lookups in real time which is why people refer to it as interpreted. This meant that not only was execution performance dependant on the variable name length (yes, an optimization in JASS is to shorten function and variable names) but also that the language performed abysmally. I have a feeling that for simple mathematics Galaxy is probably an order of magnitude faster, at least if long variable names are used.
Quote:
What ImperialGood has stated is true, however, as an addendum, the galaxy VM and scripting language is being upgraded and some of the limitations he has listed will no longer apply when we get Legacy of the Void. Specifically, you will be able to pass, store and return struct, function and array references.
However I would not recommend holding off project development until LotV. Seeing how that game is not even coming out "soon TM" so is probably still at least a year away.
Interresting. Would you clarify on how you got this information? I read a thread on hive ages ago where this one famous WC3 guy (cant remember his name) debugged the VM of the SC2 beta and first found out about all the limitations it had.
However I would not recommend holding off project development until LotV. Seeing how that game is not even coming out "soon TM" so is probably still at least a year away.
Agreed, I'm just noting it for people who may wish to refactor or have a system for a game in mind that can use this knowledge.
Interesting, can you provide more information? By "Store" do you mean there is some sort of new type that allows storing references in data tables? Passing and storing references in form of variables is already possible.
This is the script in Storm currently and it seems to be unit tests for the galaxy VM. Included are functions that are passing and return struct, array and function references, specifically one function called "testDataRefs". Yes most of it is commented out, I don't know why.
//==================================================================================================// Test functions to debug Galaxy ASM//==================================================================================================//include"TriggerLibs/DebugGalaxy2"constintlocalSizeConst=33;int[localSizeConst+1]bar;int[4]apple;int[remoteSizeConst+1]foo;voidFooBar(){inta;intb;intc;a=localSizeConst;b=remoteSizeConst;c=foo[0];c=foo[1];c=foo[2];c=foo[3];}//void TestLoopBreakpoints () {// int a = 0;// int b = 0;// int c = 0;//// while (a < 5) {// b += 6;// a += 1;// }//// a = 0;// b = 0;// for (c = 0; c < 5; c+=1) {// a += 7;// b += 9;// }//// a = 0;// b = 0;// c = 0;// for ( ; ; ) {// a += 7;// b += 9;// c += 1;// if (c > 5) {// break;// }// }//// a = 0;// b = 0;// for ( c = 0 ; ; ) {// a += 7;// b += 9;// c += 1;// if (c > 5) {// break;// }// }//// a = 0;// b = 0;// c = 0;// for ( ; c < 5 ; ) {// a += 7;// b += 9;// c += 1;// }//// a = 0;// b = 0;// c = 0;// for ( ; ; c += 1 ) {// a += 7;// b += 9;// if (c > 5) {// break;// }// }//// a = 0;// b = 0;// do {// a += 8;// b += 1;// } while (b < 5);//}//void Test () {// int i = 0;// int a = 0;// int b = 0;//// for(i = 1; i < 10; i+=1) {// a = i;// continue;// b = a;// }//// a = 19;// b = 20;//// for (;;) {// b = 7;// continue;// a = 8;// }//// for (a = 0; a < 12; a += 1) {// for (b = 0; b < 13; b += 1) {// i = 1;// continue;// i = 2;// }// }//}//void Bar() { }////string FooVS() {// return "o";//}////void Test () {// Bar();// FooVS(Bar());//}//string FooSS(string a, string b, string c) {// return a + "y" + b + "z" + c;//}////string FooBar() {// return FooSS("i",FooVS(Wait(0.5, 0)),"j");//}//string MaxReg() {// return "a" + "b" + "c" + "d" + "e" + "f" + "g" + "h" + "i" + "j" + "k" + "l" + "m" + "n" + "o" + "p" + "q" + "r" + "s" + "t" + "u" + "v" + "w" + "x" + "y" + "z" + "A" + "B";//}//struct o { // int b;// int c;// int d;//};//int gc = 7;//o ge;//int gd = 8;////typedef int[4] s;////int Func () {// int c = 5;// string d;// int e = 6;// order f;// int g = 7;//// return (c + d[2] + e + f.b + g);//}////typedef int[4] s;////int Func() {// s sf;// return sf[0];//}////int counter;////void Func(int a, int b);////typedef funcref<Func> fr;////fr funcPtr;//fr[4] funcList;////void FuncAdd(int a, int b) {// counter = counter + (a + b);//}//void FuncSub(int a, int b) {// counter = counter + (a - b);//}//void FuncMult(int a, int b) {// counter = counter + (a * b);//}//void FuncDiv(int a, int b) {// counter = counter + (a / b);//}////void TestFunc () {// funcList[0] = FuncAdd;// funcList[1] = FuncSub;// funcList[2] = FuncMult;// funcList[3] = FuncDiv;//// counter = 0;// funcList[0](1,2);// funcList[1](3,4);// funcList[3](16,8);// funcList[2](5,6);//// counter = 1;//}//struct x { string a; text b; int c; };//struct g { x a; x b; };//struct y { wave[3] a; g[2] b; x c;};////typedef x[2] h;//typedef arrayref<h>[3] z;//typedef x v;//typedef h V;//typedef V[3] X;//typedef X[4] Y;////h[3] zgf;//g[2] zgt;//y[2] zgy;////struct sx { string s; string a; int b; int c; };//typedef sx[2] th;//typedef arrayref<th>[3] tj;//th[3] gath;//th gsth;//th gsth2;////void ABTest (int a) {//}//int ACTest () {// return 0;//}//void ADTest () {//}//typedef funcref<ADTest> foo;////void AATest () {// int m = 6;// string k;// text j;// structref<x>[2] zsx;// structref<g>[2] zsg;// structref<y>[2] zsy;// structref<v> zsv;// z[2] zax;// arrayref<Y> zYx;// Y Z;// int l = 7;// tj[4] bar;//// //arrayref<foo> aa;// //structref<bar> bb;// //funcref<foob> ff;// //arrayref<x> mm;// //structref<h> ll;// funcref<AATest> jj;// funcref<AATest> kk;// foo[4] mm;//// jj = AATest;// //jj = ABTest;// //jj = ACTest;// jj = kk;// jj = mm[0];//// Z[0][1][2].c = 8;// Z[1][2][3].c = 9;//// zgy[0].c.c = 2;// zgy[0].b[0].a.c = 3;//// zsy[0] = zgy[0];// zsy[1] = zsy[0];//// gath[0][0].b = 5;// gath[0][1].c = 6;// gath[0][2].b = 7;//// gsth[0].b = 11;// gsth[0].c = 12;// gsth[1].b = 13;// gsth[1].c = 14;// gsth2[0].b = 15;// gsth2[0].c = 16;// gsth2[1].b = 17;// gsth2[1].c = 18;//// bar[0][0] = gsth;// bar[1][1] = gsth2;// bar[2][3] = gsth;//// bar[2][3][0].c = 8;// bar[1][1][1].b = 9;//}////struct s { string f; };//struct h { handle b; };////struct t { int a; text b; };////typedef t q;////int Foo() {// int a;// t b;// q c;// text k;//// k = StringToText(IntToString(a));//// return a + b.a + c.a;//}//typedef int[4] s;////void Test () {// string a;// int b;// b = a[0];//}////typedef int[4][3] tarray43;//typedef arrayref<tarray43> tar43;//typedef arrayref<tarray43>[2] taar43;////tarray43 g43one;//tarray43 g43two;////arrayref<tarray43> ar43;////arrayref<tarray43>[1] ar43;////taar43 ar43;////struct foo {// int a;// tar43 b;// taar43 c;// arrayref<tarray43> d;// arrayref<tarray43>[2][4] e;//};////structref<foo> ga;//structref<foo>[6] gb;////void TestFunc () {// g43one[2][2] = 1;// g43one[2][3] = 2;// g43one[1][0] = 3;// g43two[2][2] = 4;// g43two[2][3] = 5;// g43two[1][0] = 6;//// //ar43[0] = g43one;// //ar43[0][0][0] = ar43[0][2][2];//}//////struct sOne {// text a;// text b;//};////struct sTwo {// sOne a;//};////struct sThree {// sTwo[2] a;//};////struct sFour {// sThree a;//};////struct testTwo {// int a;// text b;// text c;// text d;// text e;// int f;//};////struct testStruct {// int a;// text b;// text c;// int d;// text e;// int f;// text[3] g; // int h;// text i;// int j;// testTwo k;// int l;// text m;// text n;// text o;// int p;// testTwo[2] q;// int r;// text s;// int t;//};////int TestFunc () {// int a;// text[17] b;// int c;// testStruct[5] d;// int e;// text f;// int g;// testStruct h;// int i;// text j;// text k;// int l;// testTwo m;// int n;// sFour o;// int p;// text q;// int r;// sFour[1][1][1][1][1] s;//// return 1;//}//int RetOne();////int ga = 3;//int gBInt = RetOne();//int gb = 4;////int gAInt = 6;////int RetOne () {// if (gAInt == 6) {// return 5;// }// else {// return 7;// }//}////string sa = "hi";//string sb = "bye" + "hello";//string sc = sa + sb;////string RetStr () {// return "how";//}////string RetStrTwo ();////const string sx = "xxxx";//const string sy = "yyyy";////const string sf = "abcdefghij";//const string sd = "wtf " + sc + " huh? " + RetStr();//const string sg = "klmnopqrst";//const string sj = RetStrTwo ();////string RetStrTwo () {// return "wtfbbqareyoukiddingme?";//}////string se = sd;//string sh = sf;//string si = sg;//string sk = sj;//string sxx = sx;//string syy = sy;////const string cStr = "hello";//const int cInt = 37;//const bool cBool = true;//const fixed cFixed = 4.5;////const string cCompStr = "hel" + "loo";//const int cCompInt = 21 + 5;//const fixed cCompFixedOne = 1.7 + 3.8;//const fixed cCompFixedTwo = 27;////int[cCompInt] gCIntArr;////int[cCompFixedOne] gCFixedArr;////string FuncStr () {// return "funcstr";//}////const string cFStr = FuncStr();////string gFStr = FuncStr();//string gCompStr = "hel" + "loo";//int gCompInt = 21 + 5;//fixed gCompFixedOne = 1.7 + 3.8;//fixed gCompFixedTwo = 27;//////int FuncInt () {//// return 1;////}////const int cfInt = FuncInt();////fixed FuncFixed () {//// return 1.0;////}////const fixed cfFixed = FuncFixed();////bool FuncBool () {//// return true;////}////const bool cfBool = FuncBool();////unit FuncUnit () {//// return null;////}////const unit cfUnit = FuncUnit();////order FuncOrder () {//// return null;////}////const order cfOrder = FuncOrder();//const order cfOrderOne = null;////const order cfOrderTwo = -1;////const order cfOrderThree = 27;////string gFStrTwo;//const string cGStrOne = gFStrTwo;//const string cGStrTwo = "hi" + gFStrTwo;////const int cArrInt = -27;//int[cArrInt] gArrCInt;//////////--------------------------------------------------------------------------------------------------//void se_init () {// trigger tse = TriggerCreate("se_func");// TriggerAddEventChatMessage(tse, c_playerAny, "scripterror", true);//}////bool se_func (bool testConds, bool runActions) {// point loc = null;// int i = 0;//// // Actions// if (!runActions) {// return true;// }//// if (DistanceBetweenPoints(loc, loc) > 0.0) {// i = 1;// }// // return true;//}//////--------------------------------------------------------------------------------------------------//void ge_init () {// trigger tge = TriggerCreate("ge_func");// TriggerAddEventChatMessage(tge, c_playerAny, "galerror", true);//}////bool ge_func (bool testConds, bool runActions) {// int i = 0;// int j = 1;//// // Actions// if (!runActions) {// return true;// }//// j = j / i;//// return true;//}//int foo(int a, int , int c) {// return (a+c);//};////int bar (int , int, int a, int , int, int c, int) {// return (a+c);//}//int (int a, int b) {// return (a + b);//}////int vFuncTwo (int a, int , int c) {// return (a + c);//}////struct sfoo {// int a;// int;// int c;//};//sfoo sf;//////struct testStruct {// int a;// char b;// int c;// bool d;// int e;//};//testStruct gs;////int[2][3][4][5][6][7][2][3] depthEight;////char b;////typedef int[5][6] adef;//adef gai;//adef[7] tai;////int a;////typedef int[5][6] adef;//int[3][4][5][6] gai;////int vFuncTwo (int a, int b, int c) {// return a;//}////void TestFuncThree (int[5][6] arr) {//}//adef vFunc (int a) {// adef arr;// //return arr;// return 0;//}//int[4] vFuncTwo (int a) {// int[4] arr;// //return arr;// return 0;//}////arrayref<adef> vFuncThree (arrayref<adef> pai) {// return pai;//}////void TestFuncTwo (arrayref<adef> pai) {// arrayref<adef> poai;// int[5][6] larr;//// //TestFuncThree(larr);// //TestFuncThree(poai);//// pai[0][4] = 102;// pai[1][3] = 103;// pai[2][2] = 109;// pai[3][1] = pai[1][3];// poai = pai;// poai[0][1] = 103;// poai[1][2] = 104;// poai[2][3] = 108;// poai = vFuncThree(pai);// poai[3][4] = gai[1][3][4][0];// poai[4][5] = pai[1][3];// poai[1][0] = poai[0][1];//////}////void TestFunc () {// int[3][4][5][6] lai;// arrayref<adef> oai;// int a;//// //a = vFuncTwo(1,2,4);//// gai[1][3][4][0] = 5;//// TestFuncTwo(lai[2][3]);// TestFuncTwo(gai[0][1]);// oai = lai[1][2];// TestFuncTwo(oai);// oai = gai[2][1];// TestFuncTwo(oai);//}//void TestFunc () {// testStruct ls;// int[10][11][12] lai;// arrayref<adef> pai;// structref<testStruct> ps;// arrayref<adef> poai;// structref<testStruct> pos;//// ls.a = 27;// ls.b = '0';// ls.c = 35;// ls.d = false;// ls.e = ls.c;// ps = ls;// ps.a = 28;// ps.b = '1';// ps.c = 36;// ps.d = true;// ps.e = ps.a;// pos = ps;//// gs.a = 27;// gs.b = '0';// gs.c = 35;// gs.d = false;// gs.e = gs.c;// ps = gs;// ps.a = 28;// ps.b = '1';// ps.c = 36;// ps.d = true;// ps.e = ps.a;// pos = ps;//// lai[0] = 101;// lai[1] = 102;// lai[7] = 108;// lai[5] = lai[7];// pai = lai;// pai[2] = 102;// pai[3] = 103;// pai[6] = 109;// pai[4] = pai[3];// poai = pai;//// gai[0] = 101;// gai[1] = 102;// gai[7] = 108;// gai[5] = gai[7];// pai = gai;// pai[2] = 102;// pai[3] = 103;// pai[6] = 109;// pai[4] = pai[3];// poai = pai;//}//int retOne () { return 1; }//int a = retOne();//int b = retOne();////int FuncOne (int a);//int FuncTwo (int a);//int FuncThree (int a, int b);//int FuncFour (int a, int b);////funcref<FuncOne> gfrOneOne;//funcref<FuncOne> gfrOneTwo;//funcref<FuncThree> gfrTwoOne;//funcref<FuncThree> gfrTwoTwo;////funcref<FuncOne> returnFROne (bool which) {// if (which) {// return FuncOne;// } else {// return FuncTwo;// }//}////funcref<FuncThree> returnFRTwo (bool which) {// if (which) {// return FuncThree;// } else {// return FuncFour;// }//}////void testFuncRefs () {// funcref<FuncOne> lfrOneOne;// funcref<FuncOne> lfrOneTwo;// funcref<FuncThree> lfrTwoOne;// funcref<FuncThree> lfrTwoTwo;// int[25] a;//// gfrOneOne = returnFROne(true);// gfrOneTwo = returnFROne(false);// gfrTwoOne = returnFRTwo(true);// gfrTwoTwo = returnFRTwo(false);//// lfrOneOne = returnFROne(false);// lfrOneTwo = returnFROne(true);// lfrTwoOne = returnFRTwo(false);// lfrTwoTwo = returnFRTwo(true);//// a[0] = gfrOneOne(5);// a[1] = gfrOneTwo(5);// a[2] = gfrTwoOne(5,4);// a[3] = gfrTwoTwo(5,3);// a[4] = lfrOneOne(5);// a[5] = lfrOneTwo(5);// a[6] = lfrTwoOne(5,4);// a[7] = lfrTwoTwo(5,3);//// gfrOneOne = FuncOne;// gfrOneTwo = gfrOneOne;//// a[8] = gfrOneOne(2);// a[9] = gfrOneTwo(2);//// gfrTwoOne = RandomInt;//// a[10] = gfrTwoOne(1, 100);// a[11] = gfrTwoOne(1, 100);// a[12] = gfrTwoOne(1, 100);// a[13] = gfrTwoOne(1, 100);//// lfrTwoOne = RandomInt;//// a[14] = lfrTwoOne(1, 100);// a[15] = lfrTwoOne(1, 100);// a[16] = lfrTwoOne(1, 100);// a[17] = lfrTwoOne(1, 100);//// gfrTwoTwo = FuncThree;// lfrTwoTwo = FuncFour;//// a[18] = gfrTwoTwo(2, 5);// a[19] = lfrTwoTwo(2, 5);//// a[20] = returnFROne(false)(2);//}////int FuncOne (int a) {// return 1;//}////int FuncTwo (int a) {// return 2;//}////int FuncThree (int a, int b) {// return (a+b);//}////int FuncFour (int a, int b) {// return (a*b);//}////funcref<FuncOne> gfu;////void FuncRefRunTimeErrorOne () {// int a;// a = gfu(2);//}////void FuncRefRunTimeErrorTwo () {// int a;// funcref<FuncOne> lfu;// a = lfu(2);//}////funcref<FuncOne> FRRTERetThree () { // funcref<FuncOne> lfu;// return lfu;//}////void FuncRefRunTimeErrorThree () { // int a;// funcref<FuncOne> lfu;// lfu = FRRTERetThree();// a = lfu(2);//}////typedef int[4] arrfour;//int[4] gafour;//int[4][3] gafourthree;//int[3][4] gathreefour;////struct sdef {// int a;// int b;// string s;//};////sdef gsone;//sdef gstwo;////arrayref<arrfour> gar;//structref<sdef> gsr;////arrayref<arrfour> garTwo;//structref<sdef> gsrTwo;////void initStructRef (structref<sdef> sr, int offset) {// sr.a = offset*10 + 2;// sr.b = offset*10 + 9;// sr.s = "foo" + IntToString(offset) + "bar";//}////void initArrayRef (arrayref<arrfour> ar, int offset) {// ar[0] = offset*10 + 0;// ar[1] = offset*10 + 7;// ar[2] = offset*10 + 3;// ar[3] = offset*10 + 6;//}////void testOne (arrayref<arrfour> par, structref<sdef> psr) {// par[0] = 370;// par[1] = 377;// par[2] = 373;// par[3] = 376;//// psr.a = 382;// psr.b = 389;// psr.s = "foo38bar";//// par = gar;// psr = gsr;// // par[0] = 390;// par[1] = 397;// par[2] = 393;// par[3] = 396;//// psr.a = 402;// psr.b = 409;// psr.s = "foo40bar";//}////arrayref<arrfour> returnAR (bool two) {// if (two) {// return garTwo;// } else {// return gar;// }//}////structref<sdef> returnSR (bool two) {// if (two) {// return gsrTwo;// } else {// return gsr;// }//}////void testDataRefs () {// int[4] lafour;// int[4][3] lafourthree;// int[3][4] lathreefour;// // sdef lsone;// sdef lstwo;//// arrayref<arrfour> lar;// structref<sdef> lsr;// arrayref<arrfour> larTwo;// structref<sdef> lsrTwo;// // initArrayRef(gafour, 11);// initArrayRef(gathreefour[0], 12);// initArrayRef(gathreefour[1], 17);// initArrayRef(gathreefour[2], 14);// // initArrayRef(lafour, 21);// initArrayRef(lathreefour[0], 22);// initArrayRef(lathreefour[1], 27);// initArrayRef(lathreefour[2], 24);//// initStructRef(gsone, 33);// initStructRef(gstwo, 34);// initStructRef(lsone, 37);// initStructRef(lstwo, 36);//// lar = lafour;// //lar = lafourthree[0]; invalid match wanted int[4] got int[3]// lar = lathreefour[0];// larTwo = gafour;// //lar = gafourthree[0]; invalid match wanted int[4] got int[3]// larTwo = gathreefour[0];//// lsr = lsone;// lsrTwo = gsone;// // gar = lathreefour[0];// gsr = lstwo;// garTwo = lafour;// gsrTwo = gsone;//// lar = lathreefour[1];// lsr = gstwo;// testOne(lar, lsr);// initArrayRef(lar, 47);// initStructRef(lsr, 45);//// gar = lathreefour[2];// garTwo = gathreefour[2];// gsr = gstwo;// gsrTwo = lsone;//// lar = returnAR(true);// initArrayRef(lar, 52);// lsr = returnSR(false);// initStructRef(lsr, 54);//// gar = lathreefour[0];// garTwo = gathreefour[0];// gsr = lstwo;// gsrTwo = gsone;//// lar = returnAR(false);// initArrayRef(lar, 57);// lsr = returnSR(true);// initStructRef(lsr, 55);//}//////arrayref<arrfour> gau;//structref<sdef> gsu;//int[10] gtemp;////void DataRefRunTimeErrorOne () {// gau[0] = 0;//}////void DataRefRunTimeErrorTwo () {// gsu.a = 0;//}////void DataRefRunTimeErrorThree () {// arrayref<arrfour> lau;// lau[0] = 0;//}////void DataRefRunTimeErrorFour () {// structref<sdef> lsu;// lsu.a = 0;//}////void FRRTERetFive () {// arrayref<arrfour> lau;// gau = lau;//}////void DataRefRunTimeErrorFive () {// FRRTERetFive();// gau[0] = 0;//}////void FRRTERetSix () {// structref<sdef> lsu;// gsu = lsu;//}////void DataRefRunTimeErrorSix () {// FRRTERetSix();// gsu.a = 0;//}////void FRRTERetSeven () {// int[4] lafour;// gau = lafour;//}////void DataRefRunTimeErrorSeven () {// FRRTERetSeven();// gau[0] = 0;//}////void FRRTERetEight () {// sdef ls;// gsu = ls;//}////void DataRefRunTimeErrorEight () {// FRRTERetEight();// gsu.a = 0;//}////void FRRTERetNine(arrayref<arrfour> pau) {// gau = pau;//}////void DataRefRunTimeErrorNine () {// arrayref<arrfour> lau;// FRRTERetNine(lau);// gau[0] = 0;//}////void FRRTERetTen(structref<sdef> psu) {// gsu = psu;//}////void DataRefRunTimeErrorTen () {// structref<sdef> lsu;// FRRTERetTen(lsu);// gsu.a = 0;//}////arrayref<arrfour> FRRTERetEleven() { // arrayref<arrfour> lau;// return lau;//}////void DataRefRunTimeErrorEleven () {// gau = FRRTERetEleven();// gau[0] = 0;//}////structref<sdef> FRRTERetTwelve() { // structref<sdef> lsu;// return lsu;//}////void DataRefRunTimeErrorTweleve () {// gsu = FRRTERetTwelve();// gsu.a = 0;//}////arrayref<arrfour> FRRTERetThirteen() {// arrayref<arrfour> lau;// return lau;//}////void DataRefRunTimeErrorThirteen () {// arrayref<arrfour> lau;// lau = FRRTERetThirteen();// lau[0] = 0;//}////structref<sdef> FRRTERetFourteen() {// structref<sdef> lsu;// return lsu;//}////void DataRefRunTimeErrorFourteen () {// structref<sdef> lsu;// lsu = FRRTERetFourteen();// lsu.a = 0;//}////void FRRetSubCallFifteen (arrayref<arrfour> lau) {// int[10] temp;// lau[0] = 3;//}////arrayref<arrfour> FRRTERetFifteen() {// arrayref<arrfour> lau;// return lau;//}////void DataRefRunTimeErrorFifteen () {// arrayref<arrfour> lau;// lau = FRRTERetFifteen();// FRRetSubCallFifteen(lau);//}////void FRRetSubCallSixteen (structref<sdef> lsu) {// int[10] temp;// lsu.a = 3;//}////structref<sdef> FRRTERetSixteen() {// structref<sdef> lsu;// return lsu;//}////void DataRefRunTimeErrorSixteen () {// structref<sdef> lsu;// lsu = FRRTERetSixteen();// FRRetSubCallSixteen(lsu);//}////arrayref<arrfour> FRRTERetSeventeen() {// int[4] lafour;// arrayref<arrfour> lau;// lau = lafour;// return lau;//}////void DataRefRunTimeErrorSeventeen () {// arrayref<arrfour> lau;// lau = FRRTERetSeventeen();// lau[0] = 3;//}////structref<sdef> FRRTERetEighteen() {// sdef ls;// structref<sdef> lsu;// lsu = ls;// return lsu;//}////void DataRefRunTimeErrorEighteen () {// structref<sdef> lsu;// lsu = FRRTERetEighteen();// lsu.a = 0;//}////void FRRetSubCallNineteen (arrayref<arrfour> lau) {// int[10] temp;// lau[0] = 3;//}////arrayref<arrfour> FRRTERetNineteen() {// int[2] a;// int[4] lafour;// arrayref<arrfour> lau;// lau = lafour;// return lau;//}////void DataRefRunTimeErrorNineteen () {// arrayref<arrfour> lau;// lau = FRRTERetNineteen();// FRRetSubCallNineteen(lau);//}////void FRRetSubCallTwenty (structref<sdef> lsu) {// int[10] temp;// lsu.a = 3;//}////structref<sdef> FRRTERetTwenty() {// int[2] a;// sdef ls;// structref<sdef> lsu;// lsu = ls;// return lsu;//}////void DataRefRunTimeErrorTwenty () {// structref<sdef> lsu;// lsu = FRRTERetTwenty();// FRRetSubCallTwenty(lsu);//}////void FRRetSubCallTwentyOne () {// int[10] temp;// gau[0] = 3;//}////void FRRTERetTwentyOne() {// int[2] a;// int[4] lafour;// gau = lafour;//}////void DataRefRunTimeErrorTwentyOne () {// FRRTERetTwentyOne();// FRRetSubCallTwentyOne();//}////void FRRetSubCallTwentyTwo () {// int[10] temp;// gsu.a = 3;//}////void FRRTERetTwentyTwo() {// int[2] a;// sdef ls;// gsu = ls;//}////void DataRefRunTimeErrorTwentyTwo () {// FRRTERetTwentyTwo();// FRRetSubCallTwentyTwo();//}////int testNum = 0;//void resetTest () {// testNum = 0;//}////void nextTest () {// testNum += 1;// if (testNum == 1) {// DebugString("testFuncRefs\n");// testFuncRefs();// }// else if (testNum == 2) {// DebugString("testDataRefs\n");// testDataRefs();// }// else if (testNum == 3) {// DebugString("FuncRefRunTimeErrorOne\n");// FuncRefRunTimeErrorOne();// } else if (testNum == 4) {// DebugString("FuncRefRunTimeErrorTwo\n");// FuncRefRunTimeErrorTwo();// } else if (testNum == 5) {// DebugString("FuncRefRunTimeErrorThree\n");// FuncRefRunTimeErrorThree();// }// else if (testNum == 6) {// DebugString("DataRefRunTimeErrorOne\n");// DataRefRunTimeErrorOne();// } else if (testNum == 7) {// DebugString("DataRefRunTimeErrorTwo\n");// DataRefRunTimeErrorTwo();// } else if (testNum == 8) {// DebugString("DataRefRunTimeErrorThree\n");// DataRefRunTimeErrorThree();// } else if (testNum == 9) {// DebugString("DataRefRunTimeErrorFour\n");// DataRefRunTimeErrorFour();// } else if (testNum == 10) {// DebugString("DataRefRunTimeErrorFive\n");// DataRefRunTimeErrorFive();// } else if (testNum == 11) {// DebugString("DataRefRunTimeErrorSix\n");// DataRefRunTimeErrorSix();// } else if (testNum == 12) {// DebugString("DataRefRunTimeErrorSeven\n");// DataRefRunTimeErrorSeven();// } else if (testNum == 13) {// DebugString("DataRefRunTimeErrorEight\n");// DataRefRunTimeErrorEight();// } else if (testNum == 14) {// DebugString("DataRefRunTimeErrorNine\n");// DataRefRunTimeErrorNine();// } else if (testNum == 15) {// DebugString("DataRefRunTimeErrorTen\n");// DataRefRunTimeErrorTen();// } else if (testNum == 16) {// DebugString("DataRefRunTimeErrorEleven\n");// DataRefRunTimeErrorEleven();// } else if (testNum == 17) {// DebugString("DataRefRunTimeErrorTweleve\n");// DataRefRunTimeErrorTweleve();// } else if (testNum == 18) {// DebugString("DataRefRunTimeErrorThirteen\n");// DataRefRunTimeErrorThirteen();// } else if (testNum == 19) {// DebugString("DataRefRunTimeErrorFourteen\n");// DataRefRunTimeErrorFourteen();// } else if (testNum == 20) {// DebugString("DataRefRunTimeErrorFifteen\n");// DataRefRunTimeErrorFifteen();// } else if (testNum == 21) {// DebugString("DataRefRunTimeErrorSixteen\n");// DataRefRunTimeErrorSixteen();// } else if (testNum == 22) {// DebugString("DataRefRunTimeErrorSeventeen\n");// DataRefRunTimeErrorSeventeen();// } else if (testNum == 23) {// DebugString("DataRefRunTimeErrorEighteen\n");// DataRefRunTimeErrorEighteen();// } else if (testNum == 24) {// DebugString("DataRefRunTimeErrorNineteen\n");// DataRefRunTimeErrorNineteen();// } else if (testNum == 25) {// DebugString("DataRefRunTimeErrorTwenty\n");// DataRefRunTimeErrorTwenty();// } else if (testNum == 26) {// DebugString("DataRefRunTimeErrorTwentyOne\n");// DataRefRunTimeErrorTwentyOne();// } else if (testNum == 27) {// DebugString("DataRefRunTimeErrorTwentyTwo\n");// DataRefRunTimeErrorTwentyTwo();// }// else {// DebugString("Done\n");// }//}//const int fooa = 10 >> 0;//const int foob = 10 << 0;//const int fooc = 10 >> 135;//const int food = 7 << 125;//const int fooe = 10 << -2;//const int foof = 10 << -2;////const int fooi = 1 / 0;////const int foog = (1 << 31);//const int fooh = foog / -1;////const fixed fooj = 1.0 / 0.0;//const fixed fook = -524288.0;//const fixed fool = 524288.0;//const fixed foom = fook / -1.0;//const fixed foon = fook / -0.5;//const fixed fooo = fool / 0.25;//const fixed foop = fool * 1.25;//const fixed fooq = fook * -1.25;////void InitMap () {//}////struct brokenS {// bool[300000] bArr;// int errorInt;//};//brokenS bgs;////typedef int[10] adef;//arrayref<adef> gpai;//arrayref<adef>[12] gapai;////void TTest () {// arrayref<adef> pai;//}////int Func (int a) {// bgs.errorInt = 237;// return (a+1);//}//int FuncTwo (int a) {// return (a+1);//}////int ReFunc (funcref<Func> fr) {// return 0;//}////int ReFuncTwo (funcref<ReFunc> frt) {//// return 0;////}////struct testStruct {// int a;// char b;// int c;// bool d;//};//testStruct gs;//structref<testStruct> gps;////typedef int idef;//int ireg;//idef idreg;////int[10] gai;////int [21][22][23] tga;//int[13][10] goaai;//int[10][11] gaai;////void TestFunc () {// testStruct ls;// int[10] lai;// arrayref<adef> pai;// structref<testStruct> ps;// int a;// funcref<Func> pfunc;// //funcref<Func> ptfunc;// //funcref<Func> ofunc;// //funcref<Func> nfunc;//// pfunc = Func;// //ptfunc = FuncTwo;// //if (Func < FuncTwo) {// // a = 1;// //}//// //idreg = idreg * ireg;//// a = pfunc(1);//// a = gaai[8][9];// a = goaai[8][9];// a = tga[8][9][10];//// ls.a = 27;// ls.b = '0';// ls.c = 35;// ls.d = false;// ps = ls;// ps.a = 28;// ps.b = '1';// ps.c = 36;// ps.d = true;//// gs.a = 27;// gs.b = '0';// gs.c = 35;// gs.d = false;// ps = gs;// ps.a = 28;// ps.b = '1';// ps.c = 36;// ps.d = true;//// lai[0] = 101;// lai[1] = 102;// lai[7] = 108;// pai = lai;// pai[0] = 102;// pai[1] = 103;// pai[7] = 109;//// gai[0] = 101;// gai[1] = 102;// gai[7] = 108;// pai = gai;// pai[0] = 102;// pai[1] = 103;// pai[7] = 109;//}//////char[4] charArray;//int[10][11] arrayDouble;//int[9][10][11] arrayTriple;//struct structOne {// int[6][7] structOneArrayDouble;// int structOneInt;//};//structOne sOne;////struct structTwo {// structOne[6][7] ssOTArray;//};//structTwo sTwo;////struct structThree {// int a;// char b;// bool c;// int[10] d;//};//structThree sThree;////void StructUse () {// structThree isThree;// int ia = 0;// int ib = 0;//// ia = sThree.a;//// ib = isThree.a;//}////void LocalSizeTest (int recurse) {// char[32767] localChars;//// localChars[0] = 'a';// localChars[32766] = 'z';//// if (recurse > 0) {// LocalSizeTest(recurse - 1);// }//}////int Foo () {// return 5;//}////int Bar () {// return 6;//}////void TestFixed () {// fixed foo = 235.783;// TriggerDebugOutput(1, FixedToText(foo, 6), false);// foo *= 272.7435;// TriggerDebugOutput(1, FixedToText(foo, 6), false);// foo /= 333.45;// TriggerDebugOutput(1, FixedToText(foo, 6), false);// foo += 11337.19;// TriggerDebugOutput(1, FixedToText(foo, 6), false);// foo -= 213414.2;// TriggerDebugOutput(1, FixedToText(foo, 6), false);//}////int FuncFoo (funcref<Foo> funcOne, funcref<Foo> funcTwo) {// return (funcOne() + funcTwo());//}////void Foob () {// funcref<Foo> funcPtrOne = Foo;// funcref<Foo> funcPtrTwo = Bar;// funcref<Foo> funcPtr;// int foo;// // //funcPtr = funcPtrOne + funcPtrTwo;//// foo = Foo() + Bar();// TriggerDebugOutput(1, IntToText(foo), false);// foo = FuncFoo(funcPtrOne, funcPtrTwo);// TriggerDebugOutput(1, IntToText(foo), false);// foo = FuncFoo(Foo, Bar);// TriggerDebugOutput(1, IntToText(foo), false);//}////void Foo () {// int a = 0;// int b = 1;// int c;//// c = b/a;//}//include "TriggerLibs/NativeLib"//include "TriggerLibs/CampaignLib"////void VFoo ();////static int SFoo (int i, int j, int k);////int Foo (int a, int b);//fixed Bar (fixed e, fixed f);////int myIntA = 731;//int myIntB;//funcref<VFoo> myFPvoid;//funcref<Foo> myFPint = Foo;//funcref<SFoo> myFPintS;//funcref<Bar> myFPfixed = Bar;//int myIntD = 732;//int myIntC;//int myIntE = 733;////fixed Bar (fixed g, fixed h) {// int z = 0;//// z = 1+2;// if (z > 2) {// z = 4+5;// }// z = Foo (z, z);//// if (z > 3) {// z = 7+8;// }//// return (g+h+z);//} ////int Foo (int k, int l);//fixed Barf (fixed m, fixed n);////int Foo (int c, int d) {// fixed p = 0;//// p = Barf (p, p);//// return (c+d+FixedToInt(p));//}////fixed Barf (fixed o, fixed p);////void InitMap () {// Foo(0,1);// myFPfixed = Bar;// myFPfixed(1.0, 2.0);// Bar(1.0, 2.0);// myFPfixed = Barf;// myFPfixed(3.0, 4.0);//// if (myFPfixed == Bar) {// Bar(1.0, 2.0);// }//// myFPvoid = InitMap;//}////fixed Barf (fixed q, fixed r) {// int y = 0;//// y = 1+2;// if (y > 2) {// y = 4+5;// }// Wait(10, 0);// if (y > 3) {// y = 11+12;// }//// return (q+r+y);//} ////void InitMap () {// //TestFunction();//// libNtve_InitLib();// libCamp_InitLib();//}////native void AchievementsDisable (int player);////int magicValue = 27;////void asdf (int zsdf) {//}////void MagicValueHelper(int a) {// magicValue=a;// return;//}////int MagicValue() {// int a;// //int b = MagicValueHelper;// //return MagicValueHelper;// if (MagicValueHelper == asdf) {// TriggerDebugOutput(1, StringToText("True"), true);// }// else {// TriggerDebugOutput(1, StringToText("False"), true);// }// TriggerDebugOutput(1, StringToText(IntToString(magicValue)), true);// if (MagicValueHelper == MagicValueHelper) {// TriggerDebugOutput(1, StringToText("True"), true);// }// else {// TriggerDebugOutput(1, StringToText("False"), true);// }// TriggerDebugOutput(1, StringToText(IntToString(magicValue)), true);// while (MagicValueHelper == asdf) {// }// TriggerDebugOutput(1, StringToText(IntToString(magicValue)), true);// a = 1;// //1;// //MagicValueHelper;// //TriggerDebugOutput(1, StringToText(IntToString(magicValue)), true);// MagicValueHelper (5);// TriggerDebugOutput(1, StringToText(IntToString(magicValue)), true);// return magicValue;//}////void InitMap () {// MagicValue();// AchievementsDisable(0);//}//void Bar( int a[1][2] ) {//}//void Foo() {//}////text foo;////void Bar (string a, int b, string c, int d) {// foo = StringExternal(a);// foo = StringExternal(a);// foo = StringExternal(a);// foo = StringExternal(a);// a = a;//}////void InitMap () {// Bar("Param/Value/lib_Camp_3358AAC6",7,"Param/Value/lib_Camp_3358AAC6",8);//}//void Bar(int a, int b, int c) {// int k;// k = 0;//}////void InitMap () {// int a;//// Bar(0, 1, 2);//// a = 47;//// AIGetTownLocation(3, 4);//}//unitgroup[57][79] foo;//unitgroup[23] bar;////void InitMap () {// unitgroup a;// unitgroup b;//// b = bar[8];// a = foo[9][10];// bar[11] = foo[12][13];// foo[17][18] = bar[19];//// if (bar[14] != foo[15][16]) {// a = b;// }//}////unitgroup Foo (unitgroup ugParam, int i1, int i2, int i3) {// return ugParam;//}//////--------------------------------------------------------------------------------------------------//unitgroup Bar (unitgroup ugParam) {// ugParam = Foo(ugParam, 0, 0, 0);// return ugParam;//}//////--------------------------------------------------------------------------------------------------//unitgroup InitMap (unitgroup ugParam, point here) {// unitgroup local1;// unitgroup local2;// int local3;// int local4;// unitgroup local5;// unitgroup local6;// unitgroup local7;// // ugParam = Bar(ugParam);// local1 = Bar(ugParam);// local1 = Bar(local2);// local2 = Bar(local1);// local6 = Bar(local5);// // ugParam = Foo(ugParam, 0, 0, 0);// ugParam = Foo(ugParam, 0, 0, 0);// // local3 = 3;// // local1 = Foo(ugParam, 0, 0, 0);// local2 = Foo(ugParam, 0, 0, 0);// // local2 = Foo(local1, 0, 0, 0);// local6 = Foo(local5, 0, 0, 0);// // if (local3 == 47) {// return local7;// }// // local5 = Foo(ugParam, 0, 0, 0);// // local4 = 4;// // if (local4 == 47) {// return local7;// }// // local6 = Foo(ugParam, 0, 0, 0);//// return local7;//}//////native trigger TriggerCreate (string s);//native void TriggerDestroy (trigger t);//native void TriggerExecute (trigger t, bool testConds, bool runActions);//native void TriggerAddEventMapInit (trigger t);//////bool TriggerEpsilon (bool checkConds, bool doActions) {// int e = 11;// e = e + 1;//}////bool TriggerDelta (bool checkConds, bool doActions) {// int d = 9;// trigger epsilon;// epsilon = TriggerCreate("TriggerEpsilon");// TriggerExecute(epsilon, false, true);// d = d + 1; //}////bool TriggerGamma (bool checkConds, bool doActions) {// int c = 7;// c = c + 1;//}////bool TriggerBeta (bool checkConds, bool doActions) {// int b = 5;// b = b + 1;//}////bool TriggerAlpha (bool checkConds, bool doActions) {// int a = 3;// a = a + 1;//}////bool TriggerMapInit (bool checkConds, bool doActions) {// trigger alpha;// trigger beta;// trigger gamma;// trigger delta;// // alpha = TriggerCreate("TriggerAlpha");// beta = TriggerCreate("TriggerBeta");// gamma = TriggerCreate("TriggerGamma");// delta = TriggerCreate("TriggerDelta");// // TriggerExecute(alpha, false, false);// TriggerExecute(beta, false, false);// TriggerExecute(gamma, false, false);// TriggerExecute(delta, false, true);//}////bool TriggerPsi (bool checkConds, bool doActions) {// int p = 19;// p = p + 1;//}////bool TriggerOmega (bool checkConds, bool doActions) {// int o = 21;// Wait(5);// o = o + 1;//}////bool TriggerGarnet (bool checkConds, bool doActions) {// int g = 23;// Wait(3);// g = g + 1;//}////bool TriggerRuby (bool checkConds, bool doActions) {// int r = 25;// Wait(1);// r = r + 1;//}////bool TriggerTopaz (bool checkConds, bool doActions) {// int t = 27;// Wait(8);// t = t + 1;//}////void InitMap () {// int i = 17;// trigger mapInit;// trigger psi;// trigger omega;// trigger garnet;// trigger ruby;// trigger topaz;// // mapInit = TriggerCreate("TriggerMapInit");// TriggerAddEventMapInit(mapInit);//// psi = TriggerCreate("TriggerPsi");// TriggerExecute(psi, false, true); // // omega = TriggerCreate("TriggerOmega");// TriggerExecute(omega, false, true);//// garnet = TriggerCreate("TriggerGarnet");// ruby = TriggerCreate("TriggerRuby");// topaz = TriggerCreate("TriggerTopaz");//// TriggerExecute(garnet, false, false);// TriggerExecute(ruby, false, false);// TriggerExecute(topaz, false, true);// // i = i + 1;//}
Interresting. Would you clarify on how you got this information? I read a thread on hive ages ago where this one famous WC3 guy (cant remember his name) debugged the VM of the SC2 beta and first found out about all the limitations it had.
One can implicitly understand the mechanics from things like the debugger. The fact that you have limited memory and code (used to be shared but they changed the architecture to separate heaps) means that there is some addressing issue going on. If you recall from computer architecture courses, instruction sets often contain various kinds of jump and memory access commands. Although they ultimately serve the same purpose, they have different limitations and performance. A short jump command might be a single word but only have a jump range of 256 bytes (executes fast, usable for most loops but not usable for long functions). A long jump or function call might use two words but have a range of an entire word so could access anything in memory. I have a feeling Galaxy only uses single word instructions of which some field must be an address pointer (why the memory limit is so low). The reason references work across stack and heap is because they are word sized themselves and stored within the address range of either a memory read or stack read op.
I have some evidence to support this in that I also tried to reverse engineer the Galaxy part of SC2. In WC3 it was highly obvious that run time name resolution was occurring by the presence of processing string literals with what appeared to be a hashing algorithm each time such a name was encountered in the script. In SC2 there were no apparent cases of run time name resolution during normal execution.
Quote:
This is the script in Storm currently and it seems to be unit tests for the galaxy VM. Included are functions that are passing and return struct, array and function references, specifically one function called "testDataRefs". Yes most of it is commented out, I don't know why.
However you must remember that Heroes of the Storm is a Blizzard made game so it can use features that are theoretically unsafe because Blizzard knows they will not intentionally break or do malicious things with them.
Galaxy originally had full C style pointer support during WoL beta. However they removed that functionality because it could be used in error prone ways. Array and Struct references also used to support global instances perfectly during the one Beta except again they could be used in an unsafe way so were removed. The issue with the pointers was pretty obvious, you could do whatever you want where ever you want just like C pointers. The references are more obscure since it is completely safe to reference globals but not safe to reference the stack (as your global ref to a local will be invalidated on return of the function and so point to invalid memory). What I do not understand is why they do not add a new reference type "staticref" which is for globals only (illegal to assign a local variable).
I can assure you that Galaxy performance is acceptable. I have written both SHA-1 and AES-like encryption for a bank save system and so far it works flawlessly.
That said, I personally hope that LotV will improve GUI support for this features as well. Its nice to have them available in script, but since GUI is the "official" Tool to program provided by Blizzard it would be nice to get access to the full feature set. I imagene its difficult for things like funcref though as it needs a function prototype as template parameter. Other things like structref should be pretty simple to do in GUI though.
Also, GUI currently does not allow creating local structs.
GUI is currently missing a lot of AI natives and constants. I am not even sure why as a simple line of custom script or even a custom script argument can add them. It also lacks the bitwise manipulator operations between bytes and ints which is slightly irritating.
While ranting, it would also be nice if the GUI to Galaxy compiler would get a little bit more intelligent.
For loops, unused else brackets, boolean checks etc. could be improved. It would also be awesome if GUI comments could transfer over to script (Instead of always having the same, unneccesary automatic comments such as "Variable initialisation", "Variable declaration" and such).
We can create our own. I created for myself (its public but mostly for myself) stuff to fix of those annoyances. Like having a condition function that doesn't involve a comparison, an actual if then (with no else, which btw, there is one, but Blizzard made it internal to the Built In Library... why I do not know).
The subfuncs and all those wonderful flags in functions/actions/conditions allow one to define anything you need in GUI, just appears no one took the time to create something and make it widely known or available. I guess this does fall to Blizzard to rectify since I doubt we can assure that people download/link a certain mod dependency to get access to all the useful extra stuff.
Also, I thought GUI comments did transfer to compile script (or at least they have for me, but I'll check again). As for eliminating the boiler plate comments, that will indeed have to be Blizzard. We do have to remember that Galaxy (as ImperialGood noted) is incredibly safe. As much as the comments make for boilerplate and seemingly clutter, they do assure no ambiguity to the code.
The problem is, even though you can define, lets say, your own custom "if then" construct, it is not possible to define an intelligent "if then else" that automatically omitts the "else" part if its empty. So if you wanted to add an else branch later on you would have to exchange "if then" with "if then else" by hand, which makes it practically useless. (This is also most likely the reason Blizzard kept the "if then" construct private)
Similar problems apply to for loops and all sorts of other constructs. One could define a perfect for loop for positive and negative increments, but not both.
To fix this we would need a more intelligent macro system which can check parameter values and use different galaxy code for specific cases.
As far as custom comments go, they definately do not transfer to script. I think the best solution would be to make this an optional setting and let the modder choose what he prefers.
That is true, but having such a thing kind of defeats the point of the GUI, which is to abstract away the actual scripting. Plus, as ungainly as the script that is generated by GUI is, it (as far as I am aware) does not cause performance issues.
Also, examining some of Blizzard built in stuff shows there is that kind of intelligent macroing already present. So one could construct such an intelligent if then else construct (There is a #IFSUBFUNC and #IFHAVSUBFUNC functions that are conditional macros, so one could use the latter to check for the existence of subfunctions/actions under the else, and therefore macro add it in. I'll see if I can make such a thing).
Edit: Here is the Action Definition and Custom script for an intelligent if then else
Used the IFHAVESUBFUNC to check for actions under the Else, if so , add in the else and the brackets. The SUBFUNCS can be added safely since if there is nothing under the Else, nothing will be placed there.
It would stand to reason that since most people get by with the code not even being viewed (everything in GUI), Blizzard did not consider it necessary or worthwhile to make what I did above, since it would just save some lines of code (wouldn't even improve performance). Most people's issues stem from data work or from simply poor algorithms or code structure (not using functions/actions in the first place, not considering concurrency and properly using Critical Sections, and the like),
Nice find. I noticed the #IFHAVESUBFUNCS macro several months ago but wasnt able to get it working in my tests.
That really makes me wonder why its not used in the default if then else construct. Even though it doesnt directly influence performance it can have quite a significant impact on code length in very large projects using thousands of conditional statements, plus it makes the code easier to read.
Im wondering if there is any easy way to replace all standard if then else conditions with the more intelligent version. I took a look at the Triggers.xml file earlier today but appearantly a simple find replace is not enough. Will have to dig deeper there.
As far as performance goes, GUI does not come with a significant penalty, but there are quite some cases where custom script can be faster. A classical example is a boolean check, which always compiles to (boolean == true) instead of just (boolean), unless using a custom defined macro. For loops also compile in a pretty bad way because they need to work for positive and negative increments, therefore the loop needs to evaluate way more conditions than necessary with every run. There are also several other details such as not being able to set the value of a function parameter within the function, which is perfectly legal in Galaxy. (This is mainly a problem for large types such as Strings, as it makes it necessary to copy the string just to change it.)
Obviously all those things arent a big deal, it would still be nice to have a better Galaxy Script quality overall, especially because it seems to be quite easy to fix in many cases and would benefit all projects.
Agreed that there are places where the default GUI code is quite.. superfluous. The conditional thing also bugged the crap out of me, so I did write a custom one that takes just a boolean and returns it (macroed). I figured the performance gain would be minimal, but yes, the extra code is annoying (the comparison default one is the most damming, because comparing values is fine, but comparing any boolean to another is a tautology and thus pointless, if you want the union (equals) that is done with AND...)
Part of the reason for the macro lack of usage is probably the non existence of documentation for it. I was able to infer what it did based on examining what it did with Blizzard (the sub funcs one checks the existence of actions under the ID of the subfunction section, as definied in the Action Defintion and macros in the 2nd paramter), but that doesn't help with someone who doesn't understand what it does.
As for replacing the standard one, you can using the built in search and replace, but it ultimately will be useless since it does NOT preserve the subfunctions (ugggh....)
Also, as I noted above, and as you verified, you can do pass and return of function pointers, so I almost wonder if you can pass a string by reference (as an arrayref) or the like, instead of pass by value.
Also, as I noted above, and as you verified, you can do pass and return of function pointers, so I almost wonder if you can pass a string by reference (as an arrayref) or the like, instead of pass by value.
I am pretty sure strings are passed by reference... Since they are complex types they have an identifier and when the reference counter is 0 then they are garbage collected. The issue with strings is they appear to be immutable so suffer the performance constraints similar to languages like JAVA which also had immutable strings. In languages like C with mutable strings you can get away with pre-defined buffer sizes to some extent and simply moving the null terminator around. You also can treat strings as arrays of characters (equal length character sets only) allowing you to perform very efficient manipulation of them.
You are absolutely right here, I completely forgot about the reference/immutable thing here. Strings do not get copied when assigning them to a variable.
Anyways, I still think it should be possible to change parameter values in GUI as otherwise it sometimes makes it necessary to create redundant local variables and perform unnecessary operations. I believe its easy to "hack" it though by using Set Variable and specifying the parameter (in custom script).
Blizzard probably thought it would be confusing to be able to alter parameters via "Set Variable" or there are other reasons I'm unaware of.
I agree. The macro system is a great feature and actually its something that can make GUI more powerful than Galaxy to some extend, just like using "Presets". (For example, it allows inline functions) It definately requires more documentation and examples.
About returning and storing references, I made some more quick tests yesterday, appearantly its NOT possible to store or return array- and structrefs, it only works for funcrefs. It sort of makes sense because otherwise one could attempt to return a reference to a local variable or similar.
Edit: This comment is kind of redundant, didn't see the remarks above about the Data Table.
Classes and dynamically allocated structs/arrays could be achieved by a pre-compiler using the global data table. Any object can be simulated by storing its members in the data table with corresponding keys.
Any references to members of the struct would also have to be compiled to DataTableGet / DataTableSet methods (or compiler generated getters/setters that invoke DataTableGet / DataTableSet).
I'm sure you can see how this would be extended to arrays, classes, etc.
There's really no point to making a compiler to do all this though; just code using DataTable in the first place. It's really not that bad.
While that absolutely works I found myself going back to just global struct arrays for most things, simply because its very tedious (especially in GUI!) and error prone to code based on data tables. For example you have to make sure that:
- When adding a new field to the "constructor" one has to make sure that it also gets deleted from the data table in the destructor, otherwise there will be resource leaks (Btw, those leaks are hard to find unless debugging the data table manually by constantly reading its value count, and even then its hard to figure out which value specifically is causing the problem)
- Always using the correct key identifiers (make sure to have no typos, maybe even use const variables to store all the key information, which means additional work)
- Data table is slower than normal variables, plus there are also many other factors slowing down this approach, such as repeating string concatination for data table keys, counters, existance checks etc.
- Needs convenience methods to access various fields (In your example smth. such as "getName()"), because its way more work to read them compared to just reading a struct member
- Bloats code size
- It can be harder to debug in the trigger debug window as the data table information is not directly accessable without reading it into a local variable first
Really, the only advantage of this approach is the dynamic memory allocation, which is why I only use it if i absolutely need it, for example if i cant know how many entries of a certain type exist. For most other things I just define a global struct array and hand-tune its size so it fits well.
That probably makes any experienced programmer cringe, but sometimes one just needs to do what works best in a real scenario. :D
That said though, I am using data tables heavily myself, for example in my container library to provide dynamic storage. Data tables are probably the best feature of the language next to the ability to work with XML.
I'm considering getting into creating a custom map in SC2, but I was wondering, are there are any current extensions of Galaxy?
By extension I mean adding scripting capability, e.g. un-requiring forward declarations, adding struct inheritance, for loops, array resizing, "++" and "--" operators, classes (essentially structs with more functionality), block commenting, etc.
Back in WC3, there was vJASS, an extension of JASS. Looking around, there used to be Andromeda and Galaxy++, but they seem to be discontinued. Has anything taken their place, or are we back to just vanilla Galaxy?
That is not possible due to how the Galaxy virtual machine works. The language is inherently very safe, to the point it is pretty much impossible to do many standard programming practices (no dynamic memory allocation, no pointers, no raw memory manipulation etc).
A pre-compiler could be made to do some of it like object orientated inheritance and block comments however you still will need to statically allocate structures. Another big issue is that being unable to return or store struct and array references makes it very difficult to link objects together without linking using a static function. Many existing extensions have got around this using the data table functionality to provide dynamic memory but this drops performance closer to interpreted languages so is not recommended for performance critical tasks.
As such it generally is better to program in Embedded C style rather than try and do anything object orientated. It can be a pain for some highly dynamic systems and code reusability but in the end you want something that works.
WC3's JASS was an interpreted language so did not have the issue of static linking like Galaxy does. It also had no pre-defined memory space meaning that you could allocate excessive memory to solve problems like dynamic allocation by using the dynamic array functionality of JASS arrays. However all this came at the cost of execution being extremely slow to the point where complex systems often ran into performance issues rather than implementation issues.
A major improvement with Galaxy is that it actually has a reasonable GUI front. You can perform most tasks using GUI and get triggers which perform similar to pure hand written Galaxy. In fact it is recommended to use GUI for most things as the editor supports it better (type safety etc). I only recommend writing Galaxy for very specialist purposes such as bit manipulation (unsupported by GUI), reference support (unsupported by GUI) and some AI natives (for some reason unsupported by GUI). This is in stark contrast with WC3's GUI where the resulting code was such a mess and disaster you had to use JASS heavily unless you wanted your map to be a poor performing pile of leaks.
You can also mix Galaxy with GUI pretty well in SC2E. Arguments can be derived from Galaxy statements so you can often get away with just a single piece of Galaxy for the functionality you want.
What ImperialGood has stated is true, however, as an addendum, the galaxy VM and scripting language is being upgraded and some of the limitations he has listed will no longer apply when we get Legacy of the Void. Specifically, you will be able to pass, store and return struct, function and array references.
I agree with everything that was said, plus I personally would never recommend using "inofficial" extentions to the language because almost all of them will be outdated and unsupported at one point. You may also run into bugs and performance issues that at the end of the day may cost you more time than the new language features can save.
Do not work against the language, instead, try developing practises which are using it to its full potential.
I'm a little bit confused by this statement. To my knowledge, Galaxy is an interpreted language, so it cannot really get "closer to interpreted performance". In my own benchmarks mathematical calculations were about 400x slower than in C, and there seems to be no compilation of the code, just syntax checking.
Interresting, can you provide more information? By "Store" do you mean there is some sort of new type that allows storing references in data tables? Passing and storing references in form of variables is already possible.
EDIT: Okay so i made some tests, and all this appears to already work in the current build of Starcraft.
It all depends on what you define as an "interpreted" language. Galaxy is interpreted in the sense that it does not run native machine code however is compiled in the sense that it runs virtual machine code. It has no pre-compile for code during save but does compile code during map load into virtual machine code. After the code is compiled everything becomes statically linked based on the virtual machine code assembly behaviour (why there are memory limits and things like that). A function name table must still be kept for dynamic resolution for triggers but that probably only resolves a function reference once during trigger creation. The result is a language that is pretty fast (comparatively). It could be made even faster by going down the Java route of JIT compiling the virtual machine code into native machine code and optimizing for near native performance but I have doubts that Blizzard is willing to put that much effort into the language. That said, looking at Heroes of the Storm it might be that pre-compiled Galaxy code (compiled on save) will be coming in LotV as there were a lot of strange files mixed with the script files.
This is in contrast to WC3 and JASS. Although it did use some kind of virtual machine for basic line execution it performed all named lookups in real time which is why people refer to it as interpreted. This meant that not only was execution performance dependant on the variable name length (yes, an optimization in JASS is to shorten function and variable names) but also that the language performed abysmally. I have a feeling that for simple mathematics Galaxy is probably an order of magnitude faster, at least if long variable names are used.
However I would not recommend holding off project development until LotV. Seeing how that game is not even coming out "soon TM" so is probably still at least a year away.
@ImperialGood: Go
Interresting. Would you clarify on how you got this information? I read a thread on hive ages ago where this one famous WC3 guy (cant remember his name) debugged the VM of the SC2 beta and first found out about all the limitations it had.
Agreed, I'm just noting it for people who may wish to refactor or have a system for a game in mind that can use this knowledge.
This is the script in Storm currently and it seems to be unit tests for the galaxy VM. Included are functions that are passing and return struct, array and function references, specifically one function called "testDataRefs". Yes most of it is commented out, I don't know why.
One can implicitly understand the mechanics from things like the debugger. The fact that you have limited memory and code (used to be shared but they changed the architecture to separate heaps) means that there is some addressing issue going on. If you recall from computer architecture courses, instruction sets often contain various kinds of jump and memory access commands. Although they ultimately serve the same purpose, they have different limitations and performance. A short jump command might be a single word but only have a jump range of 256 bytes (executes fast, usable for most loops but not usable for long functions). A long jump or function call might use two words but have a range of an entire word so could access anything in memory. I have a feeling Galaxy only uses single word instructions of which some field must be an address pointer (why the memory limit is so low). The reason references work across stack and heap is because they are word sized themselves and stored within the address range of either a memory read or stack read op.
I have some evidence to support this in that I also tried to reverse engineer the Galaxy part of SC2. In WC3 it was highly obvious that run time name resolution was occurring by the presence of processing string literals with what appeared to be a hashing algorithm each time such a name was encountered in the script. In SC2 there were no apparent cases of run time name resolution during normal execution.
However you must remember that Heroes of the Storm is a Blizzard made game so it can use features that are theoretically unsafe because Blizzard knows they will not intentionally break or do malicious things with them.
Galaxy originally had full C style pointer support during WoL beta. However they removed that functionality because it could be used in error prone ways. Array and Struct references also used to support global instances perfectly during the one Beta except again they could be used in an unsafe way so were removed. The issue with the pointers was pretty obvious, you could do whatever you want where ever you want just like C pointers. The references are more obscure since it is completely safe to reference globals but not safe to reference the stack (as your global ref to a local will be invalidated on return of the function and so point to invalid memory). What I do not understand is why they do not add a new reference type "staticref" which is for globals only (illegal to assign a local variable).
I can assure you that Galaxy performance is acceptable. I have written both SHA-1 and AES-like encryption for a bank save system and so far it works flawlessly.
Thanks for those very informative insights.
That said, I personally hope that LotV will improve GUI support for this features as well. Its nice to have them available in script, but since GUI is the "official" Tool to program provided by Blizzard it would be nice to get access to the full feature set. I imagene its difficult for things like funcref though as it needs a function prototype as template parameter. Other things like structref should be pretty simple to do in GUI though.
Also, GUI currently does not allow creating local structs.
GUI is currently missing a lot of AI natives and constants. I am not even sure why as a simple line of custom script or even a custom script argument can add them. It also lacks the bitwise manipulator operations between bytes and ints which is slightly irritating.
While ranting, it would also be nice if the GUI to Galaxy compiler would get a little bit more intelligent.
For loops, unused else brackets, boolean checks etc. could be improved. It would also be awesome if GUI comments could transfer over to script (Instead of always having the same, unneccesary automatic comments such as "Variable initialisation", "Variable declaration" and such).
We can create our own. I created for myself (its public but mostly for myself) stuff to fix of those annoyances. Like having a condition function that doesn't involve a comparison, an actual if then (with no else, which btw, there is one, but Blizzard made it internal to the Built In Library... why I do not know).
The subfuncs and all those wonderful flags in functions/actions/conditions allow one to define anything you need in GUI, just appears no one took the time to create something and make it widely known or available. I guess this does fall to Blizzard to rectify since I doubt we can assure that people download/link a certain mod dependency to get access to all the useful extra stuff.
Also, I thought GUI comments did transfer to compile script (or at least they have for me, but I'll check again). As for eliminating the boiler plate comments, that will indeed have to be Blizzard. We do have to remember that Galaxy (as ImperialGood noted) is incredibly safe. As much as the comments make for boilerplate and seemingly clutter, they do assure no ambiguity to the code.
The problem is, even though you can define, lets say, your own custom "if then" construct, it is not possible to define an intelligent "if then else" that automatically omitts the "else" part if its empty. So if you wanted to add an else branch later on you would have to exchange "if then" with "if then else" by hand, which makes it practically useless. (This is also most likely the reason Blizzard kept the "if then" construct private)
Similar problems apply to for loops and all sorts of other constructs. One could define a perfect for loop for positive and negative increments, but not both.
To fix this we would need a more intelligent macro system which can check parameter values and use different galaxy code for specific cases.
As far as custom comments go, they definately do not transfer to script. I think the best solution would be to make this an optional setting and let the modder choose what he prefers.
That is true, but having such a thing kind of defeats the point of the GUI, which is to abstract away the actual scripting. Plus, as ungainly as the script that is generated by GUI is, it (as far as I am aware) does not cause performance issues.
Also, examining some of Blizzard built in stuff shows there is that kind of intelligent macroing already present. So one could construct such an intelligent if then else construct (There is a #IFSUBFUNC and #IFHAVSUBFUNC functions that are conditional macros, so one could use the latter to check for the existence of subfunctions/actions under the else, and therefore macro add it in. I'll see if I can make such a thing).
Edit: Here is the Action Definition and Custom script for an intelligent if then else
The Action is identical to the built in one
Used the IFHAVESUBFUNC to check for actions under the Else, if so , add in the else and the brackets. The SUBFUNCS can be added safely since if there is nothing under the Else, nothing will be placed there.
It would stand to reason that since most people get by with the code not even being viewed (everything in GUI), Blizzard did not consider it necessary or worthwhile to make what I did above, since it would just save some lines of code (wouldn't even improve performance). Most people's issues stem from data work or from simply poor algorithms or code structure (not using functions/actions in the first place, not considering concurrency and properly using Critical Sections, and the like),
Nice find. I noticed the #IFHAVESUBFUNCS macro several months ago but wasnt able to get it working in my tests.
That really makes me wonder why its not used in the default if then else construct. Even though it doesnt directly influence performance it can have quite a significant impact on code length in very large projects using thousands of conditional statements, plus it makes the code easier to read.
Im wondering if there is any easy way to replace all standard if then else conditions with the more intelligent version. I took a look at the Triggers.xml file earlier today but appearantly a simple find replace is not enough. Will have to dig deeper there.
As far as performance goes, GUI does not come with a significant penalty, but there are quite some cases where custom script can be faster. A classical example is a boolean check, which always compiles to (boolean == true) instead of just (boolean), unless using a custom defined macro. For loops also compile in a pretty bad way because they need to work for positive and negative increments, therefore the loop needs to evaluate way more conditions than necessary with every run. There are also several other details such as not being able to set the value of a function parameter within the function, which is perfectly legal in Galaxy. (This is mainly a problem for large types such as Strings, as it makes it necessary to copy the string just to change it.)
Obviously all those things arent a big deal, it would still be nice to have a better Galaxy Script quality overall, especially because it seems to be quite easy to fix in many cases and would benefit all projects.
Agreed that there are places where the default GUI code is quite.. superfluous. The conditional thing also bugged the crap out of me, so I did write a custom one that takes just a boolean and returns it (macroed). I figured the performance gain would be minimal, but yes, the extra code is annoying (the comparison default one is the most damming, because comparing values is fine, but comparing any boolean to another is a tautology and thus pointless, if you want the union (equals) that is done with AND...)
Part of the reason for the macro lack of usage is probably the non existence of documentation for it. I was able to infer what it did based on examining what it did with Blizzard (the sub funcs one checks the existence of actions under the ID of the subfunction section, as definied in the Action Defintion and macros in the 2nd paramter), but that doesn't help with someone who doesn't understand what it does.
As for replacing the standard one, you can using the built in search and replace, but it ultimately will be useless since it does NOT preserve the subfunctions (ugggh....)
Also, as I noted above, and as you verified, you can do pass and return of function pointers, so I almost wonder if you can pass a string by reference (as an arrayref) or the like, instead of pass by value.
I am pretty sure strings are passed by reference... Since they are complex types they have an identifier and when the reference counter is 0 then they are garbage collected. The issue with strings is they appear to be immutable so suffer the performance constraints similar to languages like JAVA which also had immutable strings. In languages like C with mutable strings you can get away with pre-defined buffer sizes to some extent and simply moving the null terminator around. You also can treat strings as arrays of characters (equal length character sets only) allowing you to perform very efficient manipulation of them.
@ImperialGood: Go
You are absolutely right here, I completely forgot about the reference/immutable thing here. Strings do not get copied when assigning them to a variable.
Anyways, I still think it should be possible to change parameter values in GUI as otherwise it sometimes makes it necessary to create redundant local variables and perform unnecessary operations. I believe its easy to "hack" it though by using Set Variable and specifying the parameter (in custom script). Blizzard probably thought it would be confusing to be able to alter parameters via "Set Variable" or there are other reasons I'm unaware of.
@ArcaneDurandel: Go
I agree. The macro system is a great feature and actually its something that can make GUI more powerful than Galaxy to some extend, just like using "Presets". (For example, it allows inline functions) It definately requires more documentation and examples.
About returning and storing references, I made some more quick tests yesterday, appearantly its NOT possible to store or return array- and structrefs, it only works for funcrefs. It sort of makes sense because otherwise one could attempt to return a reference to a local variable or similar.
Edit: This comment is kind of redundant, didn't see the remarks above about the Data Table.
Classes and dynamically allocated structs/arrays could be achieved by a pre-compiler using the global data table. Any object can be simulated by storing its members in the data table with corresponding keys.
As an example, consider the following code:
This would be compiled to:
Any references to members of the struct would also have to be compiled to DataTableGet / DataTableSet methods (or compiler generated getters/setters that invoke DataTableGet / DataTableSet).
I'm sure you can see how this would be extended to arrays, classes, etc.
There's really no point to making a compiler to do all this though; just code using DataTable in the first place. It's really not that bad.
@MasterWrath: Go
While that absolutely works I found myself going back to just global struct arrays for most things, simply because its very tedious (especially in GUI!) and error prone to code based on data tables. For example you have to make sure that:
- When adding a new field to the "constructor" one has to make sure that it also gets deleted from the data table in the destructor, otherwise there will be resource leaks (Btw, those leaks are hard to find unless debugging the data table manually by constantly reading its value count, and even then its hard to figure out which value specifically is causing the problem)
- Always using the correct key identifiers (make sure to have no typos, maybe even use const variables to store all the key information, which means additional work)
- Data table is slower than normal variables, plus there are also many other factors slowing down this approach, such as repeating string concatination for data table keys, counters, existance checks etc.
- Needs convenience methods to access various fields (In your example smth. such as "getName()"), because its way more work to read them compared to just reading a struct member
- Bloats code size
- It can be harder to debug in the trigger debug window as the data table information is not directly accessable without reading it into a local variable first
Really, the only advantage of this approach is the dynamic memory allocation, which is why I only use it if i absolutely need it, for example if i cant know how many entries of a certain type exist. For most other things I just define a global struct array and hand-tune its size so it fits well.
That probably makes any experienced programmer cringe, but sometimes one just needs to do what works best in a real scenario. :D
That said though, I am using data tables heavily myself, for example in my container library to provide dynamic storage. Data tables are probably the best feature of the language next to the ability to work with XML.