⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 oasisscriptrubyinterpreter.cpp

📁 使用stl技术,(还没看,是听说的)
💻 CPP
字号:
#include "oasisScriptRubyInterpreter.h"
#include "oasisScriptCommon.h"
#include "oasisScriptSystem.h"
#include "oasisVector3.h"
#include "oasisQuaternion.h"


namespace Oasis {
  VALUE FuncallWrap(VALUE arg);

  //Spontaneous structure to deal with some of Ruby's internal's ugliness.
  struct RubyArgs {
    VALUE recv;
    int32 id;
    int32 n;
    VALUE *argv;
    RubyArgs(VALUE recv, int32 id, int32 n, VALUE *argv) :
              recv(recv), id(id), n(n), argv(argv) { }
  };
    
  scriptRubyInterpreter::scriptRubyInterpreter( void ) {
    //Start the ruby interpreter
    ruby_init( );
    //Load the library paths
    ruby_init_loadpath( );
  }
  scriptRubyInterpreter::~scriptRubyInterpreter( void ) {
    //kill the ruby interpreter if we need to
    ruby_finalize( );
  }   

  void scriptRubyInterpreter::reset( void ) {
    //Stop and restart the script...
    //PENDING: Is there a better way to do this?
    ruby_finalize( );
    ruby_init( );
    ruby_init_loadpath( );
  }

  bool scriptRubyInterpreter::executeScript( string &name ) {
    int error;
    
    //ruby_script sets the internal argv[0] to whatever is passed in.
    ruby_script( ( char * ) name.c_str( ) );
    rb_eval_string_protect( string( "load '" + name + "'" ).c_str( ), &error );
    if( error ) {
      if( error == 6 )
        return 0;
      
      scriptSystem::get().scriptLog( string( "While loading - " ) + name );
      printError( );
    }
    return 1;
  }
    
  void *scriptRubyInterpreter::executeString( string &command, uint8 &type ) {
    int state;
    VALUE retv_ruby;
    void *retv;
    char *error;
  
    //run the string and catch all exceptions
    retv_ruby = rb_eval_string_protect( command.c_str( ), &state );
    retv = ( void * ) convertToC( ( void * ) retv_ruby, type );
    
    if ( state ) 
      printError( );
    
    return retv;
  }
    
  void *scriptRubyInterpreter::convertFromC( void *cObject, uint8 type ) {
    VALUE retv;
    if( type == 'b' ) {
      //'b' - bool 
      bool * tmp = ( bool * ) cObject;
      retv = ( *tmp ? Qtrue : Qfalse );
    }
    else if( type == 's' ) { 
      //'s' - char *
      retv = rb_str_new2( ( char * ) cObject );
    }
    else if( type == 'd' ) {
      //notice that RUBY only has 31 bit fixnums
      //'d' - int16
      int16 * tmp = ( int16 * ) cObject;
      retv = INT2FIX( *tmp );
    }
    else if( type == 'i' ) {
      //arbitrarily sized numbers
      //'i' - int32
      int32 * tmp = ( int32 * ) cObject;
      retv = INT2NUM( *tmp );
    }
    else if( type == 'c' ) {
      //'c' - character - Seen as an one element string
       char *hold = new char[2];
       hold[0]= *( uint8 * ) cObject;
       hold[1]= 0;
       retv = rb_str_new2( hold );
       delete [] hold;
    }
    else if( type == 'f' || type == 'd' || type == 'r' ) {
      //'f' - real
      //'d' - real
      //'r' - real
      real * tmp = ( real * ) cObject;
      retv = rb_float_new( *tmp );
    }
    else if( type == 'v' ) {
      //'v' - vector(3-element array)
      vector3 *value = ( vector3 * ) cObject;
      retv = rb_ary_new3( 3, rb_float_new( value->x ),
                             rb_float_new( value->y ), 
                             rb_float_new( value->z ) );
    }
    else if( type == 'q' ) {
      //'q' - quaternion(4-element array)  
      quaternion *value = ( quaternion * ) cObject;
      retv = rb_ary_new3( 4, rb_float_new( value->w ), 
                             rb_float_new( value->x ),
                             rb_float_new( value->y ),
                             rb_float_new( value->z ) );
    }
    // This is not the fastest way to do garbage collection in ruby
    // The absolute fastest is to combine everything into an rb_ary
    // and allocate/deallocate it all at once.  However, this option
    // is not available to us unless we change the architechure.
    rb_gc_register_address(&retv);
    return ( void * ) retv;
  }
    
  void *scriptRubyInterpreter::convertToC( void *scriptObject, uint8 &type ) {
    void * retv;
    
    VALUE scriptObjectTrans = ( VALUE ) scriptObject;
    
    if( NIL_P( scriptObjectTrans ) ) {
      //nil = NULL
      retv = NULL;
      type = '0';
    }
    else if(FIXNUM_P( scriptObjectTrans )) {
      //'i' = int32
      retv = ( void * ) new int32( FIX2INT( scriptObjectTrans ) );
      type = 'i';
    }
    else if( scriptObjectTrans == Qtrue ) {
      //'b' = bool
      retv = ( void * ) new bool( true );
      type = 'b';
    }
    else if( scriptObjectTrans == Qfalse) {
      //'b' = bool
      retv = ( void * ) new bool( false ); 
      type = 'b';
    }
    else
    {
      type = TYPE( scriptObjectTrans );
      if( type == T_FLOAT )
      {
        //'r' = real
        retv = ( void * ) new real( NUM2DBL( scriptObjectTrans ) );
        type = 'r';
      }
      else if ( type == T_STRING )
      {
        //'s' = string
        char *ni,*cp;
        uint16 len;
        ni = StringValuePtr( scriptObjectTrans );
        len = strlen( ni );
        cp = new char[len];
        strcpy( cp, ni );
        retv = ( void * ) cp;
        type = 's';
      }
      else if ( type == T_BIGNUM )
      {
        //'i' = uint32, other than that, we cannot hold this value
        retv = ( void * ) new int32( NUM2INT( scriptObjectTrans ) );
        type = 'i';        
      }
      else if ( type == T_ARRAY )
      {
        uint8 len = RARRAY( scriptObjectTrans )->len;
        if( len == 3 || len == 4 ) {
          //we can combine both quaternions/vectors using some nifty ifs.
          void **vals = new ( void * )[len];
          char *types = new char[len];
          uint8 i, j;
          bool allInts = 1;
          for( i = 0; i < len; i++ ) {
            //Recursively get the value and type for each element in the array.
            vals[i] = convertToC( 
                        ( void * ) ( RARRAY( scriptObjectTrans )->ptr[i] ), type );
            types[i] = type;
            if( type == 'i' || type == 'f' || type == 'd' || type == 'r' ) 
              continue;
          
            scriptSystem::get( ).scriptLog( 
                                    string( "[!] Error, arrays returned " )+
                                    string( "of size 3 must contain only " )+
                                    string( "ints/reals to be a vector." ) );
            string strtype = " ";
            strtype[0] = type;
            scriptSystem::get( ).scriptLog( "[!] type: " +
                    scriptSystem::get( ).toString( vals[i], type ) );
            allInts = 0;
            scriptSystem::get( ).freeVariable( vals[i], type );
            break;
          }
          
          if( allInts ) {
            if(len == 3) {
              //You are responsible for freeing retv, scriptSystem::freeVariable
              // 'v' - vector
              vector3 *nvector = new vector3( );
              real xyz[3];
              //make sure the void * is casted to the right value
              for( j=0; j<3; j++ ) {
                if( types[j] == 'i')
                  xyz[j] = *( int32 *) vals[j];      
                if( types[j] == 'd' )  
                  xyz[j] = *( int16 *) vals[j];
                if( types[j] == 'r' || types[j] == 'f' )  
                  xyz[j] = *( real *) vals[j];
              }
              nvector->x = xyz[0];
              nvector->y = xyz[1];
              nvector->z = xyz[2];
              retv = ( void * ) nvector;
              type = 'v';
            }
            else {
              // 'v' - vector
              quaternion *nquat = new quaternion( );
              real wxyz[4];
              //make sure the void * is casted to the right value
              for( j=0; j<4; j++ ) {
                if( types[j] == 'i')
                  wxyz[j] = *( int32 *) vals[j];      
                if( types[j] == 'd' )  
                  wxyz[j] = *( int16 *) vals[j];
                if( types[j] == 'r' || types[j] == 'f' )  
                  wxyz[j] = *( real *) vals[j];
              }
              nquat->w = wxyz[0];
              nquat->x = wxyz[1];
              nquat->y = wxyz[2];
              nquat->z = wxyz[3];
              retv = ( void * ) nquat;
              type = 'q';
            }
          }
          else
          {
            type = '?';
            retv = scriptObject;
          }
          //i is from the above 'for' loop
          for( j=0; j<i; j++ )
          {
            scriptSystem::get( ).freeVariable( vals[j], types[j] );
          }
          delete [] vals;
          delete [] types;
        }
        else {
           scriptSystem::get( ).scriptLog( 
                                   string( "[!] Error, arrays returned " )+
                                   string( "must be of size 3(vectors) or " )+
                                   string( "4(quaternions)." ) );
          type = '?';
          retv = scriptObject;
        }
      }
      else
      {      
        //Find out the string representation of an unknown object.
     //   retv = ( void * ) convertToC( ( void * )rb_obj_as_string( 
       //                               scriptObjectTrans ), type );
        
        type = '?'; 
        //scriptSystem::get( ).scriptLog( StringValuePtr( scriptObjectTrans ) );
        //PENDING: This causes random crashes, but is really really cool.
        //PENDING: I think the problem has to do with GC in ruby, but maybe
        //PENDING: rb_obj_as_string is just not as stable as one would hope.
        //PENDING: Then again, it could also be convertToC( RB_STRING )...
      }
    }
    
    return retv;
  }
  
  void *scriptRubyInterpreter::callFunction( string &name, string &types, void ** arguments, uint8 &returntype ) {

    VALUE *arrayArgs;
    void *result;
    uint8 i;
    
    //Convert arguments to a C-style ruby array
    arrayArgs = ALLOC_N( VALUE, types.length( ) );
    for( i=0; i < types.length( ); i++ ) {
      arrayArgs[i] = ( VALUE ) arguments[i];
    }

    //PENDING: Call the function inside of a class instance?  Can we do that by
    //just extanding name to be "instance.funct"?
    //rb_cModule is ruby's globally defined namespace
    
    void * intermediate = ( void * ) executeFunction( rb_cModule,
                          rb_intern( name.c_str( ) ), 
                          types.length( ), 
                          arrayArgs ); 
    
    result = ( void * ) convertToC( intermediate, returntype );
    
    return ( void * ) result;
  }
  
  VALUE scriptRubyInterpreter::executeFunction( VALUE recv, int ID, int argc, VALUE *argv )
  {
    int error;
    RubyArgs *args= new RubyArgs( recv, ID, argc, argv ); 
    
    //Call the rb_funcall through FuncallWrap function, 
    //  while protected against exceptions :)
    VALUE result = rb_protect( FuncallWrap, ( VALUE ) args, &error );
    if( error ) 
      printError( );
    
    delete args;
    
    return result;
  }
  
  void scriptRubyInterpreter::printError( ) {
    uint8 buf;
    char *error;

    // Print the error info to stdout
    rb_p( ruby_errinfo );
    //error = ( char * ) convertToC( ( void * )rb_obj_as_string( rb_inspect( 
      //                                        ruby_errinfo ) ),buf );
    //scriptSystem::get( ).scriptLog( error );
    scriptSystem::get( ).scriptLog( StringValuePtr( ruby_errinfo ) );
    rb_backtrace( );
    //delete [] error;
  }
 
  void scriptRubyInterpreter::freeVariable( void *script_value ) {
    // dispose array and flush all elements
    VALUE rb_val = ( VALUE ) script_value;
    rb_gc_unregister_address(&rb_val);
  }
  
  //This is a callback function to deal with some of ruby's ugliness.  
  VALUE FuncallWrap(VALUE arg) {
    RubyArgs *a = ( RubyArgs * ) arg ;
      return rb_funcall2( a->recv, a->id, a->n, a->argv );
  }
  
};

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -