-Update doc/souce_doc.txt for refactoring change

-Add doc/packet_interserv.txt and doc/packet_client.txt, to refer packets
-Upd src inner documentation, map/intif mostly
-Add  common::utils::check_filepath(), fixing bugreport:9131 @loadnpc crash
-Fix pc_level_penalty_mod not using mob_class correctly
-Fix db/import/ for produce, arrow, abra, impro db...
-Upd install script (config.pl) to use mariadb for fedora/centos distrib
This commit is contained in:
lighta 2014-08-20 20:58:48 -04:00
parent 3efe5375f3
commit 402170c018
16 changed files with 1133 additions and 303 deletions

11
doc/packet_client.txt Normal file
View File

@ -0,0 +1,11 @@
//===== rAthena Documentation ================================
//= Source Documentation
//===== By: ==================================================
//= rAthena Dev Team
//===== Last Updated: ========================================
//= 20140718
//===== Description: =========================================
//= List of all packet used by login-serv (A), char-serv (H), map-serv (Z)
//= to communicate to client.
//= See packet_interserv.txt for communication between servers
//============================================================

1
doc/packet_interserv.txt Normal file

File diff suppressed because one or more lines are too long

View File

@ -218,10 +218,13 @@ The following list describes each module and its purpose.
Module Description
------ -----------
account persistence for account data
ipban offers IP banishment
login main module of login-serv
loginlog records all operations into log for login-serv
account persistence for account data
ipban offers IP banishment
login main module of login-serv
loginclif client <=> login-serv connections interface (send and receive packets to/from client)
loginchrif char-serv <=> login-serv connections interface (send and receive packets to char-serv)
logincsnlif console <=> login-serv connections interface (send and receive packets to/from console (internal buffer))
loginlog records all operations into log for login-serv
=============
| Char-serv |
@ -234,6 +237,10 @@ The following list describes each module and its purpose.
Module Description
------ -----------
char currently holds all the char-serv (EA) process
-- char_clif client <=> char-serv connections interface (send and receive packets to/from client)
-- char_csnlif console <=> char-serv connections interface (send and receive packets to/from console (internal buffer))
-- char_mapif map-serv <=> char-serv connections interface (send and receive packets to map-serv)
-- char_logif login-serv <=> char-serv connections interface (send and receive packets to login-serv)
inter main entry to inter-serv; delegates packet handling to submodules
-- int_auction handles auction request and saving
-- int_elemental handles elemental data (BL_ELE => Sorcerer mob)

View File

@ -1456,7 +1456,17 @@ int mapif_parse_GuildBasicInfoChange(int fd,int guild_id,int type,const char *da
return 0;
}
// Modification of the guild
/**
* Receive a modification request for the guildmember
* @param fd : map-serv link
* @param guild_id : Guild to alter
* @param account_id : Player aid to alter
* @param char_id : Player cid to alter
* @param type : Type of modification
* @param data : Value of modification
* @param len : Size of value
* @return
*/
int mapif_parse_GuildMemberInfoChange(int fd,int guild_id,int account_id,int char_id,int type,const char *data,int len)
{
// Could make some improvement in speed, because only change guild_member
@ -1662,7 +1672,16 @@ static int mapif_parse_GuildDeleteAlliance(struct guild *g, int guild_id, int ac
return 0;
}
// Alliance modification
/**
* Alliance modification
* @param fd
* @param guild_id1
* @param guild_id2
* @param account_id1
* @param account_id2
* @param flag
* @return
*/
int mapif_parse_GuildAlliance(int fd,int guild_id1,int guild_id2,int account_id1,int account_id2,int flag)
{
// Could speed up
@ -1710,7 +1729,7 @@ int mapif_parse_GuildAlliance(int fd,int guild_id1,int guild_id2,int account_id1
// Mark the two guild to be saved
g[0]->save_flag |= GS_ALLIANCE;
g[1]->save_flag |= GS_ALLIANCE;
return 0;
return 1;
}
// Change guild message

View File

@ -740,7 +740,12 @@ int mapif_parse_PartyLeaderChange(int fd,int party_id,int account_id,int char_id
return 1;
}
//Used to update party share level range in run time
/**
* Used to update party share level range in run time
* @param fd : map-serv link
* @param share_lvl : Max level number of difference to share exp
* @return
*/
int mapif_parse_PartyShareLevel(int fd,unsigned int share_lvl)
{
struct party_data *p;

View File

@ -64,7 +64,7 @@ void ShowDump(const void* buffer, size_t length)
ShowDebug("--- 00-01-02-03-04-05-06-07-08-09-0A-0B-0C-0D-0E-0F 0123456789ABCDEF\n");
ascii[16] = 0;
for( i = 0; i < length; i++ )
{
char c = RBUFB(buffer,i);
@ -145,10 +145,40 @@ void findfile(const char *p, const char *pat, void (func)(const char*))
}
return;
}
int check_filepath(const char* filepath){
DWORD Attribute;
if( Attribute = GetFileAttributes(filepath) ){
if( (Attribute & INVALID_FILE_ATTRIBUTES) && GetLastError() == ERROR_FILE_NOT_FOUND ) return 3;
else if( Attribute & FILE_ATTRIBUTE_DIRECTORY ) return 1;
else return 2;
}
return 0;
}
#else
#define MAX_DIR_PATH 2048
/**
* Check if the path is a directory or file
* @param filepath
* @return 1=dir, 2=file, 3=else, 0=error
*/
int check_filepath(const char* filepath){
struct stat s;
if( stat(filepath,&s) == 0 ){
if( s.st_mode & S_IFDIR ) return 1;
else if( s.st_mode & S_IFREG )return 2;
else return 3;
}
else {
return 0;
}
}
static char* checkpath(char *path, const char*srcpath)
{ // just make sure the char*path is not const
char *p=path;

View File

@ -11,6 +11,7 @@
void WriteDump(FILE* fp, const void* buffer, size_t length);
void ShowDump(const void* buffer, size_t length);
int check_filepath(const char* filepath);
void findfile(const char *p, const char *pat, void (func)(const char*));
bool exists(const char* filename);

View File

@ -4454,27 +4454,22 @@ ACMD_FUNC(hidenpc)
ACMD_FUNC(loadnpc)
{
FILE *fp;
if (!message || !*message) {
clif_displaymessage(fd, msg_txt(sd,1132)); // Please enter a script file name (usage: @loadnpc <file name>).
return -1;
}
// check if script file exists
if ((fp = fopen(message, "r")) == NULL) {
clif_displaymessage(fd, msg_txt(sd,261));
return -1;
}
fclose(fp);
// add to list of script sources and run it
npc_addsrcfile(message);
npc_parsesrcfile(message,true);
if( !npc_addsrcfile(message) //try to read file
|| !npc_parsesrcfile(message,true)
){
clif_displaymessage(fd, msg_txt(sd,261));
return -1;
}
npc_read_event_script();
clif_displaymessage(fd, msg_txt(sd,262));
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -109,7 +109,7 @@ int intif_elemental_delete(int ele_id);
int intif_elemental_save(struct s_elemental *ele);
/* @accinfo */
void intif_request_accinfo( int u_fd, int aid, int group_lv, char* query );
int intif_request_accinfo( int u_fd, int aid, int group_lv, char* query );
int CheckForCharServer(void);

View File

@ -2031,8 +2031,12 @@ static void npc_clearsrcfile(void)
npc_src_files = NULL;
}
/// Adds a npc source file (or removes all)
void npc_addsrcfile(const char* name)
/**
* Adds a npc source file (or removes all)
* @param name : file to add
* @return 0=error, 1=sucess
*/
int npc_addsrcfile(const char* name)
{
struct npc_src_list* file;
struct npc_src_list* file_prev = NULL;
@ -2040,15 +2044,17 @@ void npc_addsrcfile(const char* name)
if( strcmpi(name, "clear") == 0 )
{
npc_clearsrcfile();
return;
return 1;
}
if(check_filepath(name)!=2) return 0; //this is not a file
// prevent multiple insert of source files
file = npc_src_files;
while( file != NULL )
{
if( strcmp(name, file->name) == 0 )
return;// found the file, no need to insert it again
return 0;// found the file, no need to insert it again
file_prev = file;
file = file->next;
}
@ -2060,6 +2066,8 @@ void npc_addsrcfile(const char* name)
npc_src_files = file;
else
file_prev->next = file;
return 1;
}
/// Removes a npc source file (or all)
@ -3780,9 +3788,13 @@ static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, con
return strchr(start,'\n');// continue
}
//Read file and create npc/func/mapflag/monster... accordingly.
//@runOnInit should we exec OnInit when it's done ?
void npc_parsesrcfile(const char* filepath, bool runOnInit)
/**
* Read file and create npc/func/mapflag/monster... accordingly.
* @param filepath : Relative path of file from map-serv bin
* @param runOnInit : should we exec OnInit when it's done ?
* @return 0:error, 1:success
*/
int npc_parsesrcfile(const char* filepath, bool runOnInit)
{
int16 m, x, y;
int lines = 0;
@ -3791,12 +3803,17 @@ void npc_parsesrcfile(const char* filepath, bool runOnInit)
char* buffer;
const char* p;
if(check_filepath(filepath)!=2) { //this is not a file
ShowDebug("npc_parsesrcfile: Path doesn't seem to be a file skipping it : '%s'.\n", filepath);
return 0;
}
// read whole file to buffer
fp = fopen(filepath, "rb");
if( fp == NULL )
{
ShowError("npc_parsesrcfile: File not found '%s'.\n", filepath);
return;
return 0;
}
fseek(fp, 0, SEEK_END);
len = ftell(fp);
@ -3809,7 +3826,7 @@ void npc_parsesrcfile(const char* filepath, bool runOnInit)
ShowError("npc_parsesrcfile: Failed to read file '%s' - %s\n", filepath, strerror(errno));
aFree(buffer);
fclose(fp);
return;
return 0;
}
fclose(fp);
@ -3821,7 +3838,7 @@ void npc_parsesrcfile(const char* filepath, bool runOnInit)
// More info at http://unicode.org/faq/utf_bom.html#bom5 and http://en.wikipedia.org/wiki/Byte_order_mark#UTF-8
ShowError("npc_parsesrcfile: Detected unsupported UTF-8 BOM in file '%s'. Stopping (please consider using another character set).\n", filepath);
aFree(buffer);
return;
return 0;
}
// parse buffer
@ -3943,7 +3960,7 @@ void npc_parsesrcfile(const char* filepath, bool runOnInit)
}
aFree(buffer);
return;
return 1;
}
int npc_script_event(struct map_session_data* sd, enum npce_event type)

View File

@ -147,9 +147,9 @@ bool npc_isnear(struct block_list * bl);
int npc_get_new_npc_id(void);
void npc_addsrcfile(const char* name);
int npc_addsrcfile(const char* name);
void npc_delsrcfile(const char* name);
void npc_parsesrcfile(const char* filepath, bool runOnInit);
int npc_parsesrcfile(const char* filepath, bool runOnInit);
void do_clear_npc(void);
void do_final_npc(void);
void do_init_npc(void);

View File

@ -10091,6 +10091,7 @@ void pc_del_talisman(struct map_session_data *sd,int count,int type)
int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_class, int type)
{
int diff, rate = 100, i;
int tmp;
nullpo_ret(sd);
@ -10099,9 +10100,11 @@ int pc_level_penalty_mod(struct map_session_data *sd, int mob_level, uint32 mob_
if( diff < 0 )
diff = MAX_LEVEL + ( ~diff + 1 );
if((tmp = level_penalty[type][mob_class][diff] ) > 0 ) //use mobclass directly
return tmp;
//wtf is that for ? if penalty not found use the 1st one we found ?? ̂[lighta]
for( i = 0; i < CLASS_ALL; i++ ) {
int tmp;
if( ( tmp = level_penalty[type][i][diff] ) > 0 ) {
rate = tmp;
break;

View File

@ -73,13 +73,17 @@ struct skill_usave {
struct s_skill_db skill_db[MAX_SKILL_DB];
struct s_skill_produce_db skill_produce_db[MAX_SKILL_PRODUCE_DB];
int produce_count=0;
struct s_skill_arrow_db skill_arrow_db[MAX_SKILL_ARROW_DB];
int arrow_count=0;
struct s_skill_abra_db skill_abra_db[MAX_SKILL_ABRA_DB];
int abra_count=0;
struct s_skill_improvise_db {
uint16 skill_id;
short per;//1-10000
};
struct s_skill_improvise_db skill_improvise_db[MAX_SKILL_IMPROVISE_DB];
int impro_count=0;
struct s_skill_changematerial_db {
int itemid;
@ -88,6 +92,7 @@ struct s_skill_changematerial_db {
short qty_rate[5];
};
struct s_skill_changematerial_db skill_changematerial_db[MAX_SKILL_PRODUCE_DB];
int chgmateriel_count=0;
//Warlock
struct s_skill_spellbook_db {
@ -97,8 +102,10 @@ struct s_skill_spellbook_db {
};
struct s_skill_spellbook_db skill_spellbook_db[MAX_SKILL_SPELLBOOK_DB];
int spell_count=0;
//Guillotine Cross
struct s_skill_magicmushroom_db skill_magicmushroom_db[MAX_SKILL_MAGICMUSHROOM_DB];
int mushroom_count=0;
struct s_skill_unit_layout skill_unit_layout[MAX_SKILL_UNIT_LAYOUT];
int firewall_unit_pos;
@ -19743,21 +19750,25 @@ static bool skill_parse_row_unitdb(char* split[], int columns, int current) {
* ProduceItemID,ItemLV,RequireSkill,Requireskill_lv,MaterialID1,MaterialAmount1,......
*/
static bool skill_parse_row_producedb(char* split[], int columns, int current) {
int x,y;
int x,y,j;
int i = atoi(split[0]);
if( !i )
return false;
skill_produce_db[current].nameid = i;
skill_produce_db[current].itemlv = atoi(split[1]);
skill_produce_db[current].req_skill = atoi(split[2]);
skill_produce_db[current].req_skill_lv = atoi(split[3]);
//search if we override something, (if not j=last idx)
ARR_FIND(0, produce_count, j, skill_produce_db[j].nameid==i);
skill_produce_db[j].nameid = i;
skill_produce_db[j].itemlv = atoi(split[1]);
skill_produce_db[j].req_skill = atoi(split[2]);
skill_produce_db[j].req_skill_lv = atoi(split[3]);
for( x = 4, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_PRODUCE_RESOURCE; x += 2, y++ ) {
skill_produce_db[current].mat_id[y] = atoi(split[x]);
skill_produce_db[current].mat_amount[y] = atoi(split[x+1]);
skill_produce_db[j].mat_id[y] = atoi(split[x]);
skill_produce_db[j].mat_amount[y] = atoi(split[x+1]);
}
if(j==produce_count) produce_count++;
return true;
}
@ -19766,18 +19777,21 @@ static bool skill_parse_row_producedb(char* split[], int columns, int current) {
* SourceID,MakeID1,MakeAmount1,...,MakeID5,MakeAmount5
*/
static bool skill_parse_row_createarrowdb(char* split[], int columns, int current) {
int x,y;
int x,y,j;
int i = atoi(split[0]);
if( !i )
return false;
skill_arrow_db[current].nameid = i;
//search if we override something, (if not j=last idx)
ARR_FIND(0, arrow_count, j, skill_arrow_db[j].nameid==i);
skill_arrow_db[j].nameid = i;
for( x = 1, y = 0; x+1 < columns && split[x] && split[x+1] && y < MAX_ARROW_RESOURCE; x += 2, y++ ) {
skill_arrow_db[current].cre_id[y] = atoi(split[x]);
skill_arrow_db[current].cre_amount[y] = atoi(split[x+1]);
skill_arrow_db[j].cre_id[y] = atoi(split[x]);
skill_arrow_db[j].cre_amount[y] = atoi(split[x+1]);
}
if(j==arrow_count) arrow_count++;
return true;
}
@ -19786,7 +19800,8 @@ static bool skill_parse_row_createarrowdb(char* split[], int columns, int curren
* SkillID,PreservePoints
*/
static bool skill_parse_row_spellbookdb(char* split[], int columns, int current) {
int j;
uint16 skill_id = atoi(split[0]);
int points = atoi(split[1]);
unsigned short nameid = atoi(split[2]);
@ -19798,12 +19813,16 @@ static bool skill_parse_row_spellbookdb(char* split[], int columns, int current)
if( points < 1 )
ShowError("spellbook_db: PreservePoints have to be 1 or above! (%d/%s)\n", skill_id, skill_get_name(skill_id));
else {
skill_spellbook_db[current].skill_id = skill_id;
skill_spellbook_db[current].point = points;
skill_spellbook_db[current].nameid = nameid;
ARR_FIND(0, spell_count, j, skill_spellbook_db[j].skill_id==skill_id);
skill_spellbook_db[j].skill_id = skill_id;
skill_spellbook_db[j].point = points;
skill_spellbook_db[j].nameid = nameid;
if(j==spell_count) spell_count++;
return true;
}
return false;
}
@ -19830,9 +19849,13 @@ static bool skill_parse_row_improvisedb(char* split[], int columns, int current)
if( current >= MAX_SKILL_IMPROVISE_DB ) {
ShowError("skill_improvise_db: Maximum amount of entries reached (%d), increase MAX_SKILL_IMPROVISE_DB\n",MAX_SKILL_IMPROVISE_DB);
}
skill_improvise_db[current].skill_id = skill_id;
skill_improvise_db[current].per = j; // Still need confirm it.
ARR_FIND(0, impro_count, j, skill_improvise_db[j].skill_id==skill_id);
skill_improvise_db[j].skill_id = skill_id;
skill_improvise_db[j].per = j; // Still need confirm it.
if(j==impro_count) impro_count++;
return true;
}
@ -19840,6 +19863,7 @@ static bool skill_parse_row_improvisedb(char* split[], int columns, int current)
* SkillID
*/
static bool skill_parse_row_magicmushroomdb(char* split[], int column, int current) {
int j;
uint16 skill_id = atoi(split[0]);
if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
@ -19852,8 +19876,11 @@ static bool skill_parse_row_magicmushroomdb(char* split[], int column, int curre
ShowError("magicmushroom_db: Passive skills cannot be casted (%d/%s)\n", skill_id, skill_get_name(skill_id));
return false;
}
skill_magicmushroom_db[current].skill_id = skill_id;
ARR_FIND(0, mushroom_count, j, skill_magicmushroom_db[j].skill_id==skill_id);
skill_magicmushroom_db[j].skill_id = skill_id;
if(j==mushroom_count) mushroom_count++;
return true;
}
@ -19916,6 +19943,7 @@ static bool skill_parse_row_nonearnpcrangedb(char* split[], int column, int curr
* SkillID,DummyName,RatePerLvl
*/
static bool skill_parse_row_abradb(char* split[], int columns, int current) {
int j;
uint16 skill_id = atoi(split[0]);
if( !skill_get_index(skill_id) || !skill_get_max(skill_id) )
{
@ -19928,9 +19956,12 @@ static bool skill_parse_row_abradb(char* split[], int columns, int current) {
return false;
}
skill_abra_db[current].skill_id = skill_id;
safestrncpy(skill_abra_db[current].name, trim(split[1]), sizeof(skill_abra_db[current].name)); //store dummyname
skill_split_atoi(split[2],skill_abra_db[current].per);
ARR_FIND(0, abra_count, j, skill_abra_db[j].skill_id==skill_id);
skill_abra_db[j].skill_id = skill_id;
safestrncpy(skill_abra_db[j].name, trim(split[1]), sizeof(skill_abra_db[j].name)); //store dummyname
skill_split_atoi(split[2],skill_abra_db[j].per);
if(j==abra_count) abra_count++;
return true;
}
@ -19939,18 +19970,18 @@ static bool skill_parse_row_abradb(char* split[], int columns, int current) {
* ProductID,BaseRate,MakeAmount1,MakeAmountRate1...,MakeAmount5,MakeAmountRate5
*/
static bool skill_parse_row_changematerialdb(char* split[], int columns, int current) {
uint16 skill_id = atoi(split[0]);
uint16 product_id = atoi(split[0]);
short j = atoi(split[1]);
int x,y;
int x,y,k;
for(x=0; x<MAX_SKILL_PRODUCE_DB; x++){
if( skill_produce_db[x].nameid == skill_id )
if( skill_produce_db[x].nameid == product_id )
if( skill_produce_db[x].req_skill == GN_CHANGEMATERIAL )
break;
}
if( x >= MAX_SKILL_PRODUCE_DB ){
ShowError("changematerial_db: Not supported item ID(%d) for Change Material. \n", skill_id);
ShowError("changematerial_db: Not supported item ID(%d) for Change Material. \n", product_id);
return false;
}
@ -19958,13 +19989,16 @@ static bool skill_parse_row_changematerialdb(char* split[], int columns, int cur
ShowError("skill_changematerial_db: Maximum amount of entries reached (%d), increase MAX_SKILL_PRODUCE_DB\n",MAX_SKILL_PRODUCE_DB);
}
skill_changematerial_db[current].itemid = skill_id;
skill_changematerial_db[current].rate = j;
ARR_FIND(0, chgmateriel_count, k, skill_changematerial_db[k].itemid==product_id);
skill_changematerial_db[k].itemid = product_id;
skill_changematerial_db[k].rate = j;
for( x = 2, y = 0; x+1 < columns && split[x] && split[x+1] && y < 5; x += 2, y++ ) {
skill_changematerial_db[current].qty[y] = atoi(split[x]);
skill_changematerial_db[current].qty_rate[y] = atoi(split[x+1]);
skill_changematerial_db[k].qty[y] = atoi(split[x]);
skill_changematerial_db[k].qty_rate[y] = atoi(split[x+1]);
}
if(k==chgmateriel_count) chgmateriel_count++;
return true;
}

View File

@ -727,7 +727,7 @@ void storage_guild_storagegettocart(struct map_session_data* sd, int index, int
* Request to save guild storage
* @param account_id : account requesting the save
* @param guild_id : guild to take the guild_storage
* @param flag : ?
* @param flag : 1=char quitting, close the storage
* @return 0 : fail (no storage), 1 : success (requested)
*/
int storage_guild_storagesave(int account_id, int guild_id, int flag)

View File

@ -3,8 +3,15 @@
#TODO list :
#- don't always override import/file, sed grep ?
use CPAN;
use File::Basename;
use DBI;
use DBD::mysql;
use YAML::XS;
use Cwd;
use Getopt::Long;
use Net::Ping;
use strict;
use constant {
SERV_UID => "Serv_userid",
SERV_PW => "Serv_userpass",
@ -28,13 +35,14 @@ use constant {
MIN_PORT => 2000, #below are usually reserved for system
MAX_PORT => 65535,
};
BEGIN { #check and install module
my @aCheckModule = ("File::Basename","DBI","DBD::mysql","YAML","YAML::XS","Cwd","Getopt::Long","Net::Ping");
my @aMarkInst = ();
foreach(@aCheckModule) { eval "require $_" or push(@aMarkInst,$_); }
CPAN::install("@aMarkInst") if(@aMarkInst > 0);
foreach(@aCheckModule) { $_->import(); }
}
#BEGIN { #check and install module
# my @aCheckModule = ("File::Basename","DBI","DBD::mysql","YAML","YAML::XS","Cwd","Getopt::Long","Net::Ping");
# my @aMarkInst = ();
# foreach(@aCheckModule) { eval "require $_" or push(@aMarkInst,$_); }
# CPAN::install("@aMarkInst") if(@aMarkInst > 0);
# foreach(@aCheckModule) { $_->import(); }
#}
# setup my defaults option
my $sDsdFile = DESD_CONF_FILE;
my $sAutoyes = 0;
@ -186,13 +194,14 @@ sub InstallSoft {
print "This autoinstall feature is experimental, package name varies from distri and version, couldn't support them all\n";
$sOS = GetOS();
if($sOS eq "quit"){ print "Skipping Software installation\n"; return; }
elsif($sOS =~ /Ubuntu|Debian/i) { #tested on ubuntu 12.10
elsif($sOS =~ /Ubuntu|Debian/i) { #tested on ubuntu 12.10,13.10
my @aListSoft = ("gcc","gdb","zlibc","zlib1g-dev","make","git","mysql-client","mysql-server","mysql-common","libmysqlclient-dev","phpmyadmin","libpcre3-dev");
print "Going to install: @aListSoft\n";
system("sudo apt-get install @aListSoft");
}
elsif($sOS =~ /Fedora|CentOs/i){ #tested on fedora 18 /centos 6
my @aListSoft = ("gcc","gdb","zlib","zlib-devel","make","git","mysql-server","mysql-devel","phpmyadmin","pcre-devel");
}
elsif($sOS =~ /Fedora|CentOs/i){ #tested on fedora 18,19,20 /centos 5,6,7
my @aListSoft = ("gcc","gdb","zlib","zlib-devel","make","git","mariadb-server","maria","mariadb-devel","phpmyadmin","pcre-devel");
# my @aListSoft = ("gcc","gdb","zlib","zlib-devel","make","git","mysql-server","mysql-devel","phpmyadmin","pcre-devel");
system("sudo yum install @aListSoft");
}
elsif($sOS =~ /FreeBSD/i){ #tested on FreeBSD 9.01