c++ embedding Pawn, scripting.

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Post Reply
serengeor
Posts: 1712
Joined: Tue Jan 13, 2009 7:34 pm
Location: Lithuania

c++ embedding Pawn, scripting.

Post by serengeor »

First of all, this is my first ever made snippet to forum users, so I know It is not perfect.
I made this only to show how It is possible to embed Pawn in C++ app/game.

Download pawn sdk from their website: http://www.compuphase.com/pawn/pawn.htm
You should compile Pawn library just by including amx.c into an empy project, and you should get your static lib built in no time, It is lightweight and also extremely fast :)

So after you have built your static lib you should link it.



Now onto the fun part:
CScript.h:

Code: Select all

#ifndef CSCRIPT_H
#define CSCRIPT_H
#include <amx/amx.h>

class CScript
{
public:
    /**Executes main method in script**/
    int ExecMain();

    /**Registers native c/c++ functions to Abstract machine**/
    bool registerNatives(const AMX_NATIVE_INFO *list);

    void push(int value);

    /**Use to destroy script/delete**/
    void drop();

    friend CScript * createScript(char * filename);
protected:
    ///User should not initialize this class, instead method createScript should be used!
    CScript();
    ~CScript();
    bool LoadProgram(char * file,void * _program);
    void Error(int error);


    void *program;
    AMX amx;
    int err;
};

CScript * createScript(char * filename);
#endif // CSCRIPT_H
And theres CScript.cpp:

Code: Select all

#include "../include/CScript.h"
#include <stdio.h>
#include <string.h>
#include "amx/amxaux.h"

CScript * createScript(char * filename)
{
    long memsize = aux_ProgramSize(filename);
    if(memsize==0)
    {
        printf("Script file not found or corrupted\n");
        return NULL;
    }

    void * program = malloc(memsize);

    if (program == NULL)
    {
        printf("Memory allocation for script failed\n");
        return NULL;
    }

    CScript * my_script= new CScript();
    if(!my_script->LoadProgram(filename,program))
    {
        printf("Loading script into Abstract Machine failed\n");
        delete my_script;
        return NULL;
    }

    return my_script;

}

CScript::CScript()
{
    program=NULL;
}

CScript::~CScript()
{
    amx_Cleanup(&amx);
    if (program != NULL)
        free(this->program);

}

void CScript::drop()
{
    delete this;
}


void CScript::Error(int error)
{
    printf("Run time error %d: \"%s\"\n",
           error, aux_StrError(error));
}

bool CScript::LoadProgram(char*file,void * _program)
{

    this->err = aux_LoadProgram(&amx, file, program);
    if (err != AMX_ERR_NONE)
    {
        Error(this->err);
        return false;
    }
    return true;
}

int CScript::ExecMain()
{
    cell ret;
    this->err = amx_Exec(&amx, &ret, AMX_EXEC_MAIN);
    if (err != AMX_ERR_NONE)
        Error( this->err);

    if (ret != 0)
        printf("Returns %ld\n", (long)ret);
    return ret;
}

bool CScript::registerNatives(const AMX_NATIVE_INFO *list)
{
    err=amx_Register(&amx,list,-1);
    if (err != AMX_ERR_NONE)
    {
        Error( this->err);
        return false;
    }

    cell num=-1;
    amx_NumNatives(&amx,&num);
    printf("Registered %i native functions.\n",num);
    return true;
}
Now you have this basic class which holds Abstract machine, and some other stuff.
What you can do with this class is expose c++ native functions to pawn script.

So lets say you have these two basic functions:

Code: Select all

void print_int(int value)
{
    printf("%i\n",value);
}

void print_float(float value)
{
    printf("%f\n",value);
}
What you need to do is write some wrapper functions:

Code: Select all

static cell AMX_NATIVE_CALL n_print_int(AMX *amx, const cell *params)
{
    print_int( (int)params[1] );
}

static cell AMX_NATIVE_CALL n_print_float(AMX *amx, const cell *params)
{
    print_float( amx_ctof(params[1]) );
}
And put them into a array of AMX_NATIVE_INFO:

Code: Select all

const AMX_NATIVE_INFO print_Natives[] =
{
    { "print_int", n_print_int },
    { "print_float", n_print_float },
    {NULL,NULL}
    /* terminator */
};
Then In the main app you just create a CScript object and register that native list.

Code: Select all

int main()
{
    CScript * script = createScript("test.amx");
    if(!script)
        return -1;

    script->registerNatives(print_Natives);

    script->ExecMain();

    script->drop();

    return 0;
}
Now lets write an awesome script that would use our native functions!!!

1)Create file named test.p, and open it up with quincy(pawn script editor included in pawn SDK).
2)We should make the script aware of our native functions:
Write this into script file:

Code: Select all

native print_int(value);
native print_float(value);
Also we need to include float lib into pawn script in order to use floating point numbers.
So put this at the top of the file

Code: Select all

#include <float.inc>
3)We should write the main function which we will call from c++.

Code: Select all

main()
{
new a=10;
new Float:val = 10.111;

print_int(a);
print_float(val);
return 123;
}

Full script file(test.p) should look like this:

Code: Select all

#include <float.inc>
native print_int(value);
native print_float(value);

main()
{
new a=10;
new Float:val = 10.111;

print_int(a);
print_float(val);
return 123;
}
If I missed to explain anything, tell me, I will be glad to help :)

Now you have to compile the test.p script, you can use quincy editor to compile your script.

Put the executable, with compiled script.

And Viola, you have pawn embedded in c++ app.
Maybe this sounds complicated at first, but its actually not that hard when you get It working.

More information can be found in documentation which is included in the SDK.
Working on game: Marrbles (Currently stopped).
Post Reply