Clein 2 Posted August 10, 2014 (edited) Bueno chicos, yo vine a este emulador en exclusiva para esto!!, por lo que voy a hacer una guía a ver si me queda mas clara para los que quieran comenzar con este sistema, que esta genial!. xD Intro:Hercules Plugin Manager (de ahora en adelante como HPM) es un sistema creado en Hercules para poder modificar el código fuente del emulador sin cambiarlo directamente, usando plugins que alteren el funcionamiento pero evitando que actualizaciones del emulador nos den problemas. La documentación oficial pueden encontrarla en ingles en la wiki:http://herc.ws/wiki/Hercules_Plugin_ManagerY pueden encontrar plugins ya desarrollado por otros usuarios en la sección del foro "Downloads"http://herc.ws/board/files/category/10-plugins/ * OJO!, que de ahora en adelante, YO voy a trabajar en Linux, por lo que la compilación es distinta, para Unix y Windows! Puntos previos al código puro:Antes de comenzar a explicar los duro, voy a dejarles un par de cosas claras, tiene que compilar para que los cambios sean efectuados.* compilar el plugin que corresponde a "make plugins" en la carpeta del emulador.* Los Plugins se encuentra en la carpeta plugins del src.* Para que este sea compilado, tiene que estar anotado el nombre del archivo en src/plugins/Makefile.in en MYPLUGINS, sin .c y separado por espacios entre los que tengas.* Para que este sea utilizado por el emulador, tendremos que agregarlo en el archivo conf/plugins.conf, en plugins_list. Comenzando con nuestra creación:** Estructura general: <includes varios><struct de HPExport, con detalles del plugin><Funciones de todo tipo><Sistema de carga del plugins> Para comenzar, como lo dije anteriormente, tenemos que crear nuestro archivo en src/plugins "nombre del archivo.c", es recomendado incluir estos elementos antes de comenzar: // Copyright (c) Hercules Dev Team, licensed under GNU GPL.// See the LICENSE file// Sample Hercules Plugin#include <stdio.h>#include <stdlib.h>#include <string.h>#include "../common/HPMi.h"#include "../common/malloc.h"#include "../common/mmo.h"#include "../common/socket.h"#include "../common/strlib.h"#include "../map/clif.h"#include "../map/pc.h"#include "../map/script.h"#include "../common/HPMDataCheck.h" /* should always be the last file included! (if you don't make it last, it'll intentionally break compile time) */ para poder ir trabajando. Seguido, se suele agregar la descripción del plugin: HPExport struct hplugin_info pinfo = { "NOMBRE DEL PLUGIN", // Plugin name SERVER_TYPE_MAP,// Which server types this plugin works with? "0.1", // Plugin version HPM_VERSION, // HPM Version (don't change, macro is automatically updated)}; Ahora puedes poner las funciones que os apetesca (RECUERDA QUE ESTO ES C!, y tienes o tendrías que tener algún conocimiento básico del lenguaje, ya que entre punteros y memoria mal asignada podrías hacer que el emulador se crache!!. Existen 'funciones' básicas para trabajar (Para este ejemplo trabajare creando un comando, y a medida que pueda ire creando mas ejemplos).Por ejemplo "ACMD" que agrega un comando (cuando digo comando, me refiero a lo de los GM @baselvup,@job, etc)A esta función se suele poner el comando que deseemos ejecutar como parámetro, y las versiones mas actuales permiten la sobre carga del comando, si existe en atcommand.c, la ultima cargada sera quien se quede como fija. Para este ejemplo voy a trabajar con el primer plugin que realice, que consiste en modificar el comando @changegm para evitar que se utilice en medio de WoE. La función original de este comando es la siguiente: /*========================================== * @changegm by durf (changed by Lupus) * Changes Master of your Guild to a specified guild member *------------------------------------------*/ACMD(changegm) { struct guild *g; struct map_session_data *pl_sd; if (sd->status.guild_id == 0 || (g = sd->guild) == NULL || strcmp(g->master,sd->status.name)) { clif->message(fd, msg_txt(1181)); // You need to be a Guild Master to use this command. return false; } if( map->list[sd->bl.m].flag.guildlock || map->list[sd->bl.m].flag.gvg_castle ) { clif->message(fd, msg_txt(1182)); // You cannot change guild leaders in this map. return false; } if( !message[0] ) { clif->message(fd, msg_txt(1183)); // Usage: @changegm <guild_member_name> return false; } if((pl_sd=map->nick2sd((char *) message)) == NULL || pl_sd->status.guild_id != sd->status.guild_id) { clif->message(fd, msg_txt(1184)); // Target character must be online and be a guild member. return false; } guild->gm_change(sd->status.guild_id, pl_sd); return true;} Ahora, lo que yo quiero evitar es que me cambien el Guild Recaller en medio de WoE por lo que: if (pc_get_group_level(sd) != 99) { if (map->agit_flag == 1 || map->agit2_flag == 1) { clif->message(fd,"Estamos en WoE!, usted no puede cambiar el lider"); clif->message(fd,"Now is WoE Time!, you can't edit Guild Leader"); return false; } } **Lo dejo para que si no pertenece al grupo 99, no puede cambiar el leader, esto lo agrego a mi nueva función lo que seria: /*==========================================* ChangeGM no WoE now...*------------------------------------------*/ACMD(changegm){ struct guild *g; struct map_session_data *pl_sd; //Vanadium Edition if (pc_get_group_level(sd) != 99) { if (map->agit_flag || map->agit2_flag) { clif->message(fd,"Now is WoE Time!, you can't edit Guild Leader"); return false; } } //End Vanadium Edition if (sd->status.guild_id == 0 || (g = sd->guild) == NULL || strcmp(g->master,sd->status.name)) { clif->message(fd, msg_txt(1181)); // You need to be a Guild Master to use this command. return false; } if( map->list[sd->bl.m].flag.guildlock || map->list[sd->bl.m].flag.gvg_castle ) { clif->message(fd, msg_txt(1182)); // You cannot change guild leaders in this map. return false; } if( !message[0] ) { clif->message(fd, msg_txt(1183)); // Usage: @changegm <guild_member_name> return false; } if((pl_sd=map->nick2sd((char *) message)) == NULL || pl_sd->status.guild_id != sd->status.guild_id) { clif->message(fd, msg_txt(1184)); // Target character must be online and be a guild member. return false; } guild->gm_change(sd->status.guild_id, pl_sd); return true;} Ahora hace falta designarle como va a iniciar el plugin, existen 4 formas: [*]void plugin_init (void) [*]Se inicia cuando se inicia el server. [*]void plugin_final (void) [*]Se inicia cuando el server se cierra a si mismo. [*]void server_ready (void) [*]Se activa cuando el server esta iniciado y online [*]void server_post_final (void) [*]Antes de que se cierre por completo, los controladores de memoria siguen activos. Cada uno de estos puntos tiene sus funciones y dependiendo de lo que quieran hacer es como debe ser implementado.Para este caso lo quiero cuando se inicie el server, por lo que, lo dejare de esta forma: /* run when server starts */HPExport void plugin_init(void) { /* Para map-> */ map = GET_SYMBOL("map"); /* Para clif-> */ clif = GET_SYMBOL("clif"); /* Para para guild-> */ guild = GET_SYMBOL("guild"); /* Para msg_txt() */ atcommand = GET_SYMBOL("atcommand"); /* Agregamos el comando */ addAtcommand("changegm", changegm);} Como ya se habrán dado cuenta, deje 4 variables nuevas, map, clif, guild y atcommand, estas son tomadas de los procesos actuales, para poder usar las estructuras, una forma mas simple de verlo es que en mi codigo del comando, existen momentos en que uso "map->agit_flag" lo que tiene que hacer es llama al simbolo que tiene map, en este caso: map = GET_SYMBOL("map"); y así con el resto. Por ultimo, agregamos el comando, para eso utilizamos "addAtcommand("<comando>", <nombre función ACMD>);OJO!, que si el nombre esta repetido lo sobrecarga. addAtcommand("changegm", changegm); De esa forma, nuestro primer plugin esta listo: /* * Pluing programado por Clein para evitar el cambio de Guild Lider en medio de WoE. * v 0.1! */#include <stdio.h>#include <stdlib.h>#include <string.h>#include "../common/HPMi.h"#include "../common/malloc.h"#include "../common/mmo.h"#include "../common/socket.h"#include "../common/strlib.h"#include "../map/clif.h"#include "../map/pc.h"#include "../map/guild.h"#include "../common/HPMDataCheck.h" /* should always be the last file included! (if you don't make it last, it'll intentionally break compile time) *//** * Comando @changeGM bloqueado para su uso en WoE! */HPExport struct hplugin_info pinfo = { "ChangeGM", // Plugin name SERVER_TYPE_MAP,// Which server types this plugin works with? "0.1", // Plugin version HPM_VERSION, // HPM Version (don't change, macro is automatically updated)};/*==========================================* ChangeGM no WoE now...*------------------------------------------*/ACMD(changegm){ struct guild *g; struct map_session_data *pl_sd; //Vanadium Edition if (pc_get_group_level(sd) != 99) { if (map->agit_flag || map->agit2_flag) { clif->message(fd,"Now is WoE Time!, you can't edit Guild Leader"); return false; } } //End Vanadium Edition if (sd->status.guild_id == 0 || (g = sd->guild) == NULL || strcmp(g->master,sd->status.name)) { clif->message(fd, msg_txt(1181)); // You need to be a Guild Master to use this command. return false; } if( map->list[sd->bl.m].flag.guildlock || map->list[sd->bl.m].flag.gvg_castle ) { clif->message(fd, msg_txt(1182)); // You cannot change guild leaders in this map. return false; } if( !message[0] ) { clif->message(fd, msg_txt(1183)); // Usage: @changegm <guild_member_name> return false; } if((pl_sd=map->nick2sd((char *) message)) == NULL || pl_sd->status.guild_id != sd->status.guild_id) { clif->message(fd, msg_txt(1184)); // Target character must be online and be a guild member. return false; } guild->gm_change(sd->status.guild_id, pl_sd); return true;}/* run when server starts */HPExport void plugin_init(void) { /* Para map-> */ map = GET_SYMBOL("map"); /* Para clif-> */ clif = GET_SYMBOL("clif"); /* Para para guild-> */ guild = GET_SYMBOL("guild"); /* Para msg_txt() */ atcommand = GET_SYMBOL("atcommand"); /* Agregamos el comando */ addAtcommand("changegm", changegm);} ** Esta Guia no esta lista, y pretendo ir agregando mas funcionalidades del sistema HPM a medida que yo mismo valla programando y creando lo que requiero. Edited August 10, 2014 by Clein 1 jaBote reacted to this Quote Share this post Link to post Share on other sites
Clein 2 Posted August 10, 2014 (edited) En esta segunda parte, os mostrare como gatillar una función antes o después de que ocurra alguna de las funciones del emulador. *Les voy a poner todo el código luego iré explicando: - Este plugin consiste en que cuando mueres, te suelta un cráneo, pero no como el del pk mode que se activa o en todos los mapas, o en mapas pvp, pero no solo cuando mueres entre pk, es siempre que mueres, este esta dedicado solo a si te mata un player. /* * Pluing programado por Clein para que cuando un player muera * en modo Pvp suelte un craneo, y solo sea cuando esten dos player * peliando * v 0.1! */#include <stdio.h>#include <stdlib.h>#include <string.h> #include "../common/HPMi.h"#include "../common/malloc.h"#include "../common/mmo.h"#include "../common/socket.h"#include "../common/strlib.h"#include "../map/clif.h"#include "../map/pc.h"#include "../map/guild.h" #include "../common/HPMDataCheck.h" /* should always be the last file included! (if you don't make it last, it'll intentionally break compile time) */ HPExport struct hplugin_info pinfo = { "CraneosPlayer", // Plugin name SERVER_TYPE_MAP,// Which server types this plugin works with? "0.1", // Plugin version HPM_VERSION, // HPM Version (don't change, macro is automatically updated)}; uint16 GetWord(uint32 val, int idx) { switch( idx ) { case 0: return (uint16)( (val & 0x0000FFFF) ); case 1: return (uint16)( (val & 0xFFFF0000) >> 0x10 ); default: ShowDebug("GetWord: invalid index (idx=%d)n", idx); return 0; }} int dead_event_skull(struct map_session_data *sd,struct block_list *src){ if( !map->list[sd->bl.m].flag.gvg_castle ) { //Vemos si es humano. if(src && src->type == BL_PC) { //Vemos que no sea el mismo struct map_session_data *ssd = (struct map_session_data *)src; if(sd->status.char_id != ssd->status.char_id ) { struct item item_tmp; memset(&item_tmp,0,sizeof(item_tmp)); item_tmp.nameid=ITEMID_SKULL_; item_tmp.identify=1; item_tmp.card[0]=CARD0_CREATE; item_tmp.card[1]=0; item_tmp.card[2]=GetWord(sd->status.char_id,0); // CharId item_tmp.card[3]=GetWord(sd->status.char_id,1); map->addflooritem(&item_tmp,1,sd->bl.m,sd->bl.x,sd->bl.y,0,0,0,0); } } } return 1;} HPExport void plugin_init (void) { /* Para map-> */ map = GET_SYMBOL("map"); /* Se ejecuta antes del evento muerte */ addHookPre("pc->dead",dead_event_skull);}En esta ocasión comenzare de abajo, por el "HPExport void plugin_init (void) {"tomare el simbolo de map para poder verificar el mapa donde esta el player, que este mercado como WoE Map. Luego le diré con "addHookPre" que PREVIO a que se gatille la función "pc_dead" de pc.c, ejecute la función "dead_event_skull" Entonces de esta forma, paso y analizo la funcion "dead_event_skull" Si se dan cuenta tiene los mismos parametros que la funcion pc_dead de pc.c, ya que requiero de los argumentos. - pregunto si esta en mapa marcado como gvg cast "if( !map->list[sd->bl.m].flag.gvg_castle )", - Luego si mato a un humano "if(src && src->type == BL_PC)" - Creo una estructura con la persona que muere la llamo ssd, y luego pregunto por sus char_id, en caso que sean distintos prosigo. "struct map_session_data *ssd = (struct map_session_data *)src; if(sd->status.char_id != ssd->status.char_id )" Ahora, agrege la funcion "GetWord" solo para que el compilador no tirara advertencia de declaración implícita, pero no es 100% requerido (me parece D=! xD) Bueno eso es todo, la gracia de este plugin es jugar con un evento que se gatilla antes del evento general de la muerte, pueden generar uno antes o después de cualquier función prácticamente. Edited August 10, 2014 by Clein 1 jaBote reacted to this Quote Share this post Link to post Share on other sites
Relzz 32 Posted August 29, 2014 Buena guia =) Quote Share this post Link to post Share on other sites
GallaZ 1 Posted September 15, 2014 Clein, muchas gracias! Es muy útil esta guía. Quote Share this post Link to post Share on other sites