/* Copyright (c) 2008-09 Joshua R. Rodgers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //NOTE: Please see aw_php_data to see PHP specific implementation of data handling methods. //Include phppointers.i to allow for us to use pass-by-reference in PHP. %include "phppointers.i" %{ #define PHP_AW_VER "0.1.9.77" #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef WIN32 #include #else #ifdef WIN32 #icnlude #endif #endif #include "php_ini.h" PHP_INI_BEGIN() PHP_INI_ENTRY("aw.bind_address", " ", PHP_INI_ALL, NULL) PHP_INI_END() %} //Display info for phpinfo(); %pragma(php) phpinfo=" char version[10]; sprintf(version, \"%d\", AW_BUILD); php_info_print_table_start(); php_info_print_table_row(2, \"php_aw version\", PHP_AW_VER); php_info_print_table_row(2, \"AW SDK support\", \"enabled\"); php_info_print_table_row(2, \"AW SDK version\", version); php_info_print_table_end(); DISPLAY_INI_ENTRIES(); " //We have the extension call aw_init and aw_term for us. Very fun stuff! %minit { REGISTER_INI_ENTRIES(); char *ipAddress = INI_STR("aw.bind_address"); int rc = (strlen(ipAddress) == 1 ? aw_init(AW_BUILD) : aw_init_bind(AW_BUILD, inet_addr(ipAddress))); if(rc != 0) zend_error(E_ERROR, "AWSDK: Failed to initialize AWSDK (Reason: %d).", rc); } %mshutdown { aw_term(); UNREGISTER_INI_ENTRIES(); } //PHP specific implementation of aw_traffic_count(). //This uses PHP references. //Parameter 1 = traffic in //Parameter 2 = traffic out extern int aw_traffic_count (int *REF, int *REF); //Second parameter is read_only, just a reference type. extern int aw_world_attribute_get (AW_ATTRIBUTE attribute, int *REF, char* value); //These are helper functions required to make events and callbacks operate. They are not wrapped into PHP. %{ //Following are some #defines for making my life easier. Reduces code repetition. // If I change this, it changes them all. :) #define EVENT_CASE(event) \ case event: \ return SETTER(event, handle_event); #define CALLBACK_CASE(callback) \ case callback: \ return SETTER(callback, handle_callback); #define VALIDATE_USER_DATA(user_data) \ if(user_data == NULL) \ zend_error(E_ERROR, "There was an internal fatal memory error."); //I don't ALWAYS use #define! This makes debugging much easier. const int max_str = 255; //Specify a structure that contains arrays for holding the event and callback handler names. struct data { char events[AW_MAX_EVENT][max_str]; char callbacks[AW_MAX_CALLBACK][max_str]; void *user_data; //Reserved for user data on a per-instance basis. }; data global_data; //Haha, deal with it! //This method is used to handle and dispatch events. template void handle_event() { zval funcname, retval; INIT_ZVAL(funcname); INIT_ZVAL(retval); if(INSTANCE == false) { ZVAL_STRING(&funcname, global_data.events[EVENT_TYPE], 1); } else { data *instance_data = (data *)aw_user_data(); VALIDATE_USER_DATA(instance_data) ZVAL_STRING(&funcname, instance_data->events[EVENT_TYPE], 1); } TSRMLS_FETCH(); if(call_user_function(CG(function_table), NULL, &funcname, &retval, 0, NULL TSRMLS_CC) == SUCCESS) { zval_dtor(&retval); } else { zend_error(E_ERROR, "AWSDK: Method %s could not be found for dispatching the event.", global_data.events[EVENT_TYPE]); } } //This method is used to handle and dispatch callbacks. template void handle_callback(int error) { zval funcname, retval; zval *args[1]; INIT_ZVAL(funcname); INIT_ZVAL(retval); MAKE_STD_ZVAL(args[0]); ZVAL_LONG(args[0], error); if(INSTANCE == false) { ZVAL_STRING(&funcname, global_data.callbacks[CALLBACK_TYPE], 1); } else { data *instance_data = (data *)aw_user_data(); VALIDATE_USER_DATA(instance_data) ZVAL_STRING(&funcname, instance_data->callbacks[CALLBACK_TYPE], 1); } TSRMLS_FETCH(); if(call_user_function(CG(function_table), NULL, &funcname, &retval, 1, args TSRMLS_CC) == SUCCESS) { zval_dtor(&retval); } else { zend_error(E_ERROR, "AWSDK: Method %s could not be found for dispatching the callback.", global_data.callbacks[CALLBACK_TYPE]); } } //Templated function for setting instance and global events accordingly. template int set_event(AW_EVENT_ATTRIBUTE event, const char *name) { if(name == NULL) { return SETTER(event, (void(*)())NULL); } else { switch(event) { EVENT_CASE( AW_EVENT_AVATAR_ADD ) EVENT_CASE( AW_EVENT_AVATAR_CHANGE ) EVENT_CASE( AW_EVENT_AVATAR_DELETE ) EVENT_CASE( AW_EVENT_CELL_BEGIN ) EVENT_CASE( AW_EVENT_CELL_OBJECT ) EVENT_CASE( AW_EVENT_CELL_END ) EVENT_CASE( AW_EVENT_CHAT ) EVENT_CASE( AW_EVENT_OBJECT_ADD ) EVENT_CASE( AW_EVENT_OBJECT_DELETE ) EVENT_CASE( AW_EVENT_UNIVERSE_ATTRIBUTES ) EVENT_CASE( AW_EVENT_UNIVERSE_DISCONNECT ) EVENT_CASE( AW_EVENT_WORLD_ATTRIBUTES ) EVENT_CASE( AW_EVENT_WORLD_INFO ) EVENT_CASE( AW_EVENT_WORLD_DISCONNECT ) EVENT_CASE( AW_EVENT_OBJECT_CLICK ) EVENT_CASE( AW_EVENT_OBJECT_SELECT ) EVENT_CASE( AW_EVENT_AVATAR_CLICK ) EVENT_CASE( AW_EVENT_URL ) EVENT_CASE( AW_EVENT_URL_CLICK ) EVENT_CASE( AW_EVENT_TELEPORT ) EVENT_CASE( AW_EVENT_ADMIN_WORLD_INFO ) EVENT_CASE( AW_EVENT_ADMIN_WORLD_DELETE ) EVENT_CASE( AW_EVENT_TERRAIN_BEGIN ) EVENT_CASE( AW_EVENT_TERRAIN_DATA ) EVENT_CASE( AW_EVENT_TERRAIN_END ) EVENT_CASE( AW_EVENT_CONSOLE_MESSAGE ) EVENT_CASE( AW_EVENT_TERRAIN_CHANGED ) EVENT_CASE( AW_EVENT_BOTGRAM ) EVENT_CASE( AW_EVENT_TOOLBAR_CLICK ) EVENT_CASE( AW_EVENT_USER_INFO ) EVENT_CASE( AW_EVENT_NOISE ) EVENT_CASE( AW_EVENT_CAMERA ) EVENT_CASE( AW_EVENT_BOTMENU ) EVENT_CASE( AW_EVENT_OBJECT_BUMP ) EVENT_CASE( AW_EVENT_ENTITY_ADD ) EVENT_CASE( AW_EVENT_ENTITY_CHANGE ) EVENT_CASE( AW_EVENT_ENTITY_DELETE ) EVENT_CASE( AW_EVENT_ENTITY_RIDER_ADD ) EVENT_CASE( AW_EVENT_ENTITY_RIDER_DELETE ) EVENT_CASE( AW_EVENT_ENTITY_RIDER_CHANGE ) EVENT_CASE( AW_EVENT_AVATAR_RELOAD ) EVENT_CASE( AW_EVENT_ENTITY_LINKS ) EVENT_CASE( AW_EVENT_HUD_CLICK ) EVENT_CASE( AW_EVENT_HUD_CREATE ) EVENT_CASE( AW_EVENT_HUD_DESTROY ) EVENT_CASE( AW_EVENT_HUD_CLEAR ) EVENT_CASE( AW_EVENT_CAV_DEFINITION_CHANGE ) EVENT_CASE( AW_EVENT_WORLD_CAV_DEFINITION_CHANGE ) default: zend_error(E_WARNING, "AWSDK: This event is not available to the Active Worlds SDK. It has been ignored."); return -1; } } } //Templated function for setting instance and global callbacks accordingly. template int set_callback(AW_CALLBACK callback, const char *name) { if(name == NULL) { SETTER(callback, (void(*)(int))NULL); } else { switch(callback) { CALLBACK_CASE( AW_CALLBACK_CREATE ) CALLBACK_CASE( AW_CALLBACK_LOGIN ) CALLBACK_CASE( AW_CALLBACK_ENTER ) CALLBACK_CASE( AW_CALLBACK_OBJECT_RESULT ) CALLBACK_CASE( AW_CALLBACK_LICENSE_ATTRIBUTES ) CALLBACK_CASE( AW_CALLBACK_LICENSE_RESULT ) CALLBACK_CASE( AW_CALLBACK_CITIZEN_ATTRIBUTES ) CALLBACK_CASE( AW_CALLBACK_CITIZEN_RESULT ) CALLBACK_CASE( AW_CALLBACK_QUERY ) CALLBACK_CASE( AW_CALLBACK_UNIVERSE_EJECTION ) CALLBACK_CASE( AW_CALLBACK_UNIVERSE_EJECTION_RESULT ) CALLBACK_CASE( AW_CALLBACK_ADDRESS ) CALLBACK_CASE( AW_CALLBACK_WORLD_EJECTION ) CALLBACK_CASE( AW_CALLBACK_WORLD_EJECTION_RESULT ) CALLBACK_CASE( AW_CALLBACK_ADMIN_WORLD_LIST ) CALLBACK_CASE( AW_CALLBACK_ADMIN_WORLD_RESULT ) CALLBACK_CASE( AW_CALLBACK_DELETE_ALL_OBJECTS_RESULT ) CALLBACK_CASE( AW_CALLBACK_CELL_RESULT ) CALLBACK_CASE( AW_CALLBACK_RELOAD_REGISTRY ) CALLBACK_CASE( AW_CALLBACK_ATTRIBUTES_RESET_RESULT ) CALLBACK_CASE( AW_CALLBACK_ADMIN ) CALLBACK_CASE( AW_CALLBACK_TERRAIN_SET_RESULT ) CALLBACK_CASE( AW_CALLBACK_TERRAIN_NEXT_RESULT ) CALLBACK_CASE( AW_CALLBACK_TERRAIN_DELETE_ALL_RESULT ) CALLBACK_CASE( AW_CALLBACK_TERRAIN_LOAD_NODE_RESULT ) CALLBACK_CASE( AW_CALLBACK_BOTGRAM_RESULT ) CALLBACK_CASE( AW_CALLBACK_USER_LIST ) CALLBACK_CASE( AW_CALLBACK_BOTMENU_RESULT ) CALLBACK_CASE( AW_CALLBACK_CAV ) CALLBACK_CASE( AW_CALLBACK_CAV_RESULT ) CALLBACK_CASE( AW_CALLBACK_WORLD_INSTANCE ) CALLBACK_CASE( AW_CALLBACK_HUD_RESULT ) CALLBACK_CASE( AW_CALLBACK_AVATAR_LOCATION ) CALLBACK_CASE( AW_CALLBACK_OBJECT_QUERY ) CALLBACK_CASE( AW_CALLBACK_WORLD_CAV_RESULT ) CALLBACK_CASE( AW_CALLBACK_WORLD_CAV ) default: zend_error(E_WARNING, "AWSDK: This callback is not available to the Active Worlds SDK. It has been ignored."); return -1; } } } %} //These are wrapper methods needed to make certain features like instance creation and event setting work. //The following rename the wrapper methods to their equivalent C names for the PHP environment %rename(aw_create) aw_php_create; %rename(aw_create_resolved) aw_php_create_resolved; %rename(aw_server_admin) aw_php_server_admin; %rename(aw_destroy) aw_php_destroy; %rename(aw_instance_set) aw_php_instance_set; %rename(aw_instance) aw_php_instance; %rename(aw_event_set) aw_php_event_set; %rename(aw_event) aw_php_event; %rename(aw_instance_event_set) aw_php_instance_event_set; %rename(aw_instance_event) aw_php_instance_event; %rename(aw_callback_set) aw_php_callback_set; %rename(aw_callback) aw_php_callback; %rename(aw_instance_callback_set) aw_php_instance_callback_set; %rename(aw_instance_callback) aw_php_instance_callback; //Prototype methods for SWIG to do some special PHP typemapping on. extern int aw_php_create(const char *domain, int port, size_t &REF); extern int aw_php_create_resolved(unsigned long addr, int port, size_t &REF); extern int aw_php_server_admin(const char *domain, int port, char *password, size_t &REF); %inline %{ //Define constructors for instances, since PHP doesn't play too nicely with aw_create() being directly exposed. int aw_php_create(const char *domain, int port, size_t &instance) { void *temp; int rc = aw_create(domain, port, &temp); aw_user_data_set(new data()); if(&instance != NULL) { instance = (size_t)temp; } return rc; } //Creates an instance using a previously resolved address. int aw_php_create_resolved(unsigned long addr, int port, size_t &instance) { void *temp; int rc = aw_create_resolved(addr, port, &temp); aw_user_data_set(new data()); if(&instance != NULL) { instance = (size_t)temp; } return rc; } //Creates a server admin instance. int aw_php_server_admin (const char* domain, int port, char* password, size_t &instance) { void *temp; int rc = aw_server_admin(domain, port, password, &temp); aw_user_data_set(new data()); if(&instance != NULL) { instance = (size_t)temp; } return rc; } //Destroys the current instance (also performs some memory cleanup. :) int aw_php_destroy() { delete (data *)aw_user_data(); return aw_destroy(); } //Sets the current instance int aw_php_instance_set(size_t instance) { return aw_instance_set((void *)instance); } //Gets the current instance size_t aw_php_instance() { return (size_t)aw_instance(); } //Sets the name of the event handler for the given event. int aw_php_event_set(AW_EVENT_ATTRIBUTE event, const char *name) { if(strlen(name) < max_str) { strcpy(global_data.events[event], name); } else { zend_error(E_ERROR, "AWSDK: Buffer overflow detected."); return 0; } return set_event(event, name); } //Returns the name of the event handler associated with the given event. const char *aw_php_event(AW_EVENT_ATTRIBUTE event) { return global_data.events[event]; } //Sets an instance specific event. int aw_php_instance_event_set(AW_EVENT_ATTRIBUTE event, const char *name) { data *instance_data = (data *)aw_user_data(); VALIDATE_USER_DATA(instance_data) if(strlen(name) < max_str) { strcpy(instance_data->events[event], name); } else { zend_error(E_ERROR, "AWSDK: Buffer overflow detected."); return -1; } return set_event(event, name); } //Gets an instance specific event. const char *aw_php_instance_event(AW_EVENT_ATTRIBUTE event) { data *instance_data = (data *)aw_user_data(); VALIDATE_USER_DATA(instance_data) return instance_data->events[event]; } //Sets the name of the callback handler for the given callback. int aw_php_callback_set(AW_CALLBACK callback, const char *name) { if(strlen(name) < max_str) { strcpy(global_data.callbacks[callback], name); } else { zend_error(E_ERROR, "AWSDK: Buffer overflow detected."); return -1; } return set_callback(callback, name); } //Sets an instance specific callback. int aw_php_instance_callback_set(AW_CALLBACK callback, const char *name) { data *instance_data = (data *)aw_user_data(); VALIDATE_USER_DATA(instance_data) if(strlen(name) < max_str) { strcpy(instance_data->callbacks[callback], name); } else { zend_error(E_ERROR, "AWSDK: Buffer overflow detected."); return -1; } return set_callback(callback, name); } //Gets an instance specific callback. const char *aw_php_callback(AW_CALLBACK callback) { data *instance_data = (data *)aw_user_data(); VALIDATE_USER_DATA(instance_data) return instance_data->callbacks[callback]; } %} //This is for aw_terrain_set %{ template class smart_array { private: T *array; unsigned int _size; public: smart_array() : array(NULL), _size(0) {} smart_array(unsigned int size) : array(new T[size]), _size(size) {} ~smart_array() { if(array != NULL) delete[] array; } T &operator[](unsigned int position) { if(position < 0 || position > _size) { zend_error(E_WARNING, "AWSDK: Out of bounds. Call ignored."); //return NULL; } return array[position]; } const T &operator[](unsigned int position) const { if(position < 0 || position > _size) { zend_error(E_WARNING, "AWSDK: Out of bounds. Call ignored."); //return NULL; } return array[position]; } T *ptr() { return array; } unsigned int get_size() const { return _size; } }; %} //Remap zval to smart_array %typemap(in) smart_array & { zval *in = *$input; if(in->type == IS_ARRAY) { HashTable *array = Z_ARRVAL_P(in); HashPosition p_position; int position = 1; unsigned int array_size = zend_hash_num_elements(array); smart_array temp = smart_array(array_size+1); //$1 = &temp; zval **data; for(zend_hash_internal_pointer_reset_ex(array, &p_position); zend_hash_get_current_data_ex(array, (void **) &data, &p_position) == SUCCESS; zend_hash_move_forward_ex(array, &p_position)) { if(Z_TYPE_PP(data) == IS_LONG) { temp[position] = Z_LVAL_PP(data); ++position; } else { zend_error(E_ERROR, "AWSDK: Element was not an integer."); return; } } $1 = &temp; } else { zend_error(E_ERROR, "AWSDK: Expected array."); return; } } %rename(aw_terrain_set) aw_php_terrain_set; %inline { int aw_php_terrain_set(int x, int z, int texture, smart_array &heights) { return aw_terrain_set(x, z, heights.get_size()-1, texture, heights.ptr()+1); } } //These are all tomporarily unvailable until I come up with a good way to handle them /*aw_user_data_set and aw_user_data extern int aw_zip (unsigned char* data_out, unsigned int* len_out, unsigned char* data_in, unsigned int len_in); extern int aw_unzip (unsigned char* data_out, unsigned int* len_out, unsigned char* data_in, unsigned int len_in); */