Jump to content

AnnieRuru

Script Developers
  • Content Count

    1677
  • Joined

  • Last visited

  • Days Won

    247

Reputation Activity

  1. Upvote
    AnnieRuru reacted to bWolfie in [Guide] Mapcache Generation 2018   
    As of Release v2018.03.13, the method to generate mapcache for Hercules has changed. A lot of people. myself included, were confused as to how it works. In this thread, I will do my best to explain the new way to generate your cache. This guide is intended to help people using the old system, not to aid new people (i.e. never generated mapcache before), so if you are new, some of it may seem like alien talk.
    Old (your source predates [is older than] Feb 18, 2018)
    In the old system, there were two ways to generate mapcache
    You could run the mapcache executable in Hercules root folder. Or use a program such as WeeMapCache to edit in your required mapcache. These two methods would generate or alter your required mapcache located in db/[pre-re or re]/map_cache.dat. However, they are no longer supported.
    New (your source is using Release v2018.03.13 or newer)
    The new system involves the use of the new 'mapcache' plugin to generate files. Some quick points:
    db/[pre-re or re]/map_cache.dat has been dropped (no longer supported). In its place are individual .mcache files for every map located in maps/[pre-re or re]/ Mapcache executable has been removed. Replaced with the mapcache plugin (src/plugins/mapcache.c). How to generate the mapcache?
    Same as before, check conf/map/maps.conf and db/map_index.txt have all the maps you want to cache. Your maps need to exist somewhere in your repository! There are two ways for the plugin to find them:
    a. Place all your maps, including resnametable.txt, inside the data folder of your Hercules repo. I.e. Hercules/data/prontera.gat/gnd/gnd/rsw (note: I forget if all three files are needed).
    b. OR Configure your conf/grf-files.txt to tell it where to find your GRF(s) which contains your maps. Build the mapcache plugin. On linux, this can be done by running the following command:
    make plugin.mapcache
    If using MSVC, compile as you would any other plugin. Execute plugin. This can be done using the following command:
    ./map-server --load-plugin mapcache [param]
    In windows, just remove the './' and run the commands in your command prompt. The params:
    The first thing you should do is run
    ./map-server --load-plugin mapcache --help
    A list of usable parameters will appear. Here are the ones you need to know for mapcache:
    [Info]:   --convert-old-mapcache         Converts an old db/pre-re/map_cache.dat file to the new format. [Mapcache] [Info]:   --rebuild-mapcache             Rebuilds the entire mapcache folder (maps/pre-re/), using db/map_index.txt as index. [Mapcache] [Info]:   --map <name>                   Rebuilds an individual map's cache into maps/pre-re/ (usage: --map <map_name_without_extension>). [Mapcache] [Info]:   --fix-md5                      Updates the checksum for the files in maps/pre-re/, using db/map_index.txt as index (see PR #1981). [Mapcache] ./map-server --load-plugin mapcache --convert-old-mapcache
    Rebuild all the .mcache files using your old db/[pre-re or re]map_cache.dat file.
    NOTE: You should only run this command when you have an old map_cache.dat file you need to convert.
    ./map-server --load-plugin mapcache --rebuild-mapcache
    Rebuild all the .mcache files using your map files specified in step 2 of generation.
    NOTE: You should only run this command in special circumstances. This erases ALL the existing mapcache and rebuilds it with whatever files you provide it. If you don't have the required files, the build will fail and you will be left with missing mapcache files, meaning you won't be able to access those particular maps.
    ./map-server --load-plugin mapcache --map <name>
    Rebuild the .mcache file for the map name you specify. E.g. if you replace <name> with prontera, the maps/[pre-re or re]/prontera.mcache file will be rebuilt.
    NOTE: This is the best command to run, as it only caches a single map at a time.
    ./map-server --load-plugin mapcache --fix-md5
    I don't know what checksum is for.
    The End
    Feel free to ask for help here. I'll try to answer questions re: mapcache if possible. And if you think anything needs correcting or added, let me know.
    Hope this helps!
  2. Upvote
    AnnieRuru got a reaction from Cabrera in Advance SQL commands   
    1. When to use escape_sql script command
     
    .
    input .@haha$; dispbottom .@haha$; dispbottom escape_sql(.@haha$); it doesn't has much differences, because it only affect 3 special characters
    ' <- single quotation mark
    " <- double quotation mark
    \ <- left slash
    if I input -> haha"lala'hehe <-
    it will return -> haha\"lala\'hehe <-
    this is what we call, Escape a character
    in hercules script, we also know we can use " symbol in any string input
    mes "Susan says :\" Today I ate 3 eggs \"."; where in the game client, you can see the " symbol in the npc msg box
    let's say I have a sql script like this
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input name, I'll show you all characters name it has on that player's account"; input .@name$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `name` LIKE '"+ .@name$ +"'", .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this script has a possibility to be hacked
    because to perform sql injection, I can enclose the string with quotation mark, then use another sql command to hack
    BUT with an escape_sql command, if the user want to enclose the string with quotation mark to hack the script
    the escape_sql command escaped the string, the quotation mark the user input will be escaped
    thus the script will become impossible to hack
    just now that script was for string input
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input account ID, I'll show you all characters name it has on that player's account"; input .@aid$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `account_id` = "+ escape_sql(.@aid$), .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this is another stupid case.
    1. the scripter use string input while the script just needed a number
    2. even with escape_sql command over there, there is no quotation mark at all
    yes this script also has a risk to be hack
    because escape_sql only escape quotation mark.
    that hacker don't even have to input quotation mark because it is a number
    and an injection query can be sent without any quotation mark input
    there are 2 ways to solve this
    either use numeric variable for the input command
    or enclose that ....
    ..... WHERE `account_id` = '"+ escape_sql(.@aid$) +"'", .... with single quotation mark, when the hacker input a quotation mark will be escaped by escape_sql command
    Reference : https://www.w3schools.com/sql/sql_injection.asp
    escape_sql command for another thing is
    if the player register their names containing ' or ", these characters are escaped
    only happens when the server have no restriction on the creation of players name
    hercules\conf\char\char-server.conf
    // Manage possible letters/symbol in the name of charater. Control character (0x00-0x1f) are never accepted. Possible values are: // NOTE: Applies to character, party and guild names. // 0: no restriction (default) // 1: only letters/symbols in 'name_letters' option. // 2: Letters/symbols in 'name_letters' option are forbidden. All others are possibles. name_option: 1  
    and this was what happened to my SQL dota pvpladder script
    Silo's Babies <-- this is a guild name
    you can see the 5th string has a single quotation mark
    with escape_sql command, that string will turn into
    Silo\'s Babies <-- the quotation mark is escaped when send to sql query
  3. Like
    AnnieRuru got a reaction from Bringer in Advance SQL commands   
    6. How to show the current rank of the player
    Question : This is the part of the script, output as below
    .@nb = query_sql("SELECT `name`, `kills` FROM `pvpladder` ORDER BY `kills` DESC LIMIT 5", .@name$, .@kills); for ( .@i = 0; .@i < .@nb; .@i++ ) mes "No."+(.@i+1)+" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; No.1 [Alice] ~ 19 kills No.2 [Emily] ~ 11 kills No.3 [Irene] ~ 11 kills No.4 [Brittany] ~ 8 kills No.5 [Fiona] ~ 7 kills  2nd place and 3rd place has the same amount of kills, how do I make both of them display as 2nd place like this ?
    No.1 [Alice] ~ 19 kills No.2 [Emily] ~ 11 kills No.2 [Irene] ~ 11 kills No.4 [Brittany] ~ 8 kills No.5 [Fiona] ~ 7 kills  
    Answer :
    Method no.1: Convert the table into InnoDB will return the result faster. Allow to use OFFSET
    .@nb = query_sql("SELECT `name`, `kills`, FIND_IN_SET(`kills`, (SELECT GROUP_CONCAT(`kills` ORDER BY `kills` DESC) FROM `pvpladder`)) FROM `pvpladder` ORDER BY `kills` DESC LIMIT 5", .@name$, .@kills, .@rank); for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+ .@rank[.@i] +" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; Method no.2: This method return result faster than method 1 in huge table. Not allow to use OFFSET
    .@query$ = "SELECT `name`, IF(@d=t.`kills`, @r, @r:=@i), @d:=t.`kills`, @i:=@i+1 "; .@query$ += "FROM `pvpladder` t, (SELECT @d:=0, @r:=0, @i:=1)q "; .@query$ += "ORDER BY `kills` DESC LIMIT 5"; .@nb = query_sql(.@query$, .@name$, .@rank, .@kills, .@dummy); for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+ .@rank[.@i] +" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; .
    .
    Question : How do I show the current ranking of the player ?
    mes "Your kills -> "+ .@kills; mes "Your rank -> "+ .@rank; Answer :
    query_sql "SELECT `kills`, 1+(SELECT COUNT(1) FROM `pvpladder` t1 WHERE t1.`kills` > t2.`kills`) FROM `pvpladder` t2 WHERE `char_id` = "+ getcharid(0), .@kills, .@rank; Remember to index the `kills` field
     
    Reference : https://dba.stackexchange.com/questions/13703/get-the-rank-of-a-user-in-a-score-table
    .
     
  4. Upvote
    AnnieRuru got a reaction from Rebel in Advance SQL commands   
    5. How to do IF-ELSE in SQL query ?
    Question : I have a PVP ladder script that runs on Points system. Each kill plus 1 point and each death minus 1 point.
    The problem is, this query will make the points go into negative value if the player is being kill repeatedly
    query_sql "UPDATE `pvp_points` SET `points` = `points` - 1 WHERE `char_id` = "+ getcharid(0); How do I make the points stop at 0 if the player is already at 0 points ?
     
    Answer :
    query_sql "UPDATE `pvp_points` SET `points` = IF(`points` = 0, 0, `points` - 1) WHERE `char_id` = "+ getcharid(0); query_sql "UPDATE `pvp_points` SET `points` = (CASE WHEN `points` = 0 THEN 0 ELSE `points` - 1 END) WHERE `char_id` = "+ getcharid(0);  
    Explanations:
    similar to hercules script language,
    if (<condition>) <execute true condition>; else <execute false condition>; in SQL language
    IF(<condition>, <execute true condition>, <execute false condition>) CASE WHEN <condition> THEN <execute true condition> ELSE <execute false condition> END  
    Reference : https://www.w3schools.com/sql/func_mysql_if.asp
    https://stackoverflow.com/questions/63447/how-do-i-perform-an-if-then-in-an-sql-select
    5a. How to update multiple rows on different conditions in a single query
    This query will update multiple rows on different condition
    UPDATE `pvpladder` SET `points` = CASE WHEN `char_id` = 150000 THEN `points` +1 WHEN `char_id` = 150001 THEN `points` -1 END WHERE `char_id` IN (150000,150001); Reference : https://stackoverflow.com/questions/20255138/sql-update-multiple-records-in-one-query
  5. Upvote
    AnnieRuru reacted to Easycore in Character name appear on top of character head   
    Personally I prefer it on top lol
  6. Upvote
    AnnieRuru reacted to Ai4rei in What skills should I learn in order to understand Client development?   
    Depending on how much you want DIY on the client, the learning curve can be steep.
    Imho the primary skills are knowing how the client works from the player perspective (i.e. the whole functionality available, without modifying it) and knowing the subject you want to include/exclude. Thus, if you want to deal with bots, you should also know how bots work and be able to setup and use one.
    Further down, you have to deal with C++, machine code generated from C++ (assembler), disassembling, debugging and Win32 API. If you stumble upon graphics stuff, GDI and DirectX 7 are also topics of interest. If you deal with network code and packets, some background knowledge of TCP is also helpful.
    Note, that some things are better to be done server-side rather than client-side for two reasons:
    Players can undo your client-side changes, but not server-side. Server-side is easier to edit, since you have all the source, whereas in the client you have only the gory machine code.
  7. Upvote
    AnnieRuru got a reaction from Radian in Advance SQL commands   
    4. AUTO_INCREMENT
    CREATE TABLE `support_ticket` ( `id` INT(11) AUTO_INCREMENT, `title` VARCHAR(70), `message` VARCHAR(255), PRIMARY KEY (`id`) ) ENGINE = InnoDB; In this kind of query that has AUTO_INCREMENT, many people do ....
    $support_ticket_id++; query_sql "INSERT INTO `support_ticket` VALUES ( "+ $support_ticket_id +", '"+ escape_sql(.@title$) ... can be optimize .... using NULL
    query_sql "INSERT INTO `support_ticket` VALUES ( NULL, '"+ escape_sql(.@title$) ... can retrieve the last row with
    query_sql "SELECT MAX(`id`) FROM `support_ticket`", .@id; // ----- OR ----- query_sql "SELECT LAST_INSERT_ID()", .@id;  
    Question : This question was asked on eathena forum board
    One of my friend touched my custom table and the AUTO_INCREMENT has jump off the value
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 25854 | <data set 4> | 25855 | <data set 5> | 25856 | <data set 6> I want to make the value return back to normal as shown
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 4 | <data set 4> | 5 | <data set 5> | 6 | <data set 6> How to do this WITHOUT losing any of the current data ?
    Answer: The trick is ... just drop that column and rebuild it
    ALTER TABLE `inventory` DROP COLUMN `id`; ALTER TABLE `inventory` ADD COLUMN `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; convert the table into MyISAM will process the query much faster
  8. Upvote
    AnnieRuru got a reaction from Rebel in Advance SQL commands   
    6. How to show the current rank of the player
    Question : This is the part of the script, output as below
    .@nb = query_sql("SELECT `name`, `kills` FROM `pvpladder` ORDER BY `kills` DESC LIMIT 5", .@name$, .@kills); for ( .@i = 0; .@i < .@nb; .@i++ ) mes "No."+(.@i+1)+" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; No.1 [Alice] ~ 19 kills No.2 [Emily] ~ 11 kills No.3 [Irene] ~ 11 kills No.4 [Brittany] ~ 8 kills No.5 [Fiona] ~ 7 kills  2nd place and 3rd place has the same amount of kills, how do I make both of them display as 2nd place like this ?
    No.1 [Alice] ~ 19 kills No.2 [Emily] ~ 11 kills No.2 [Irene] ~ 11 kills No.4 [Brittany] ~ 8 kills No.5 [Fiona] ~ 7 kills  
    Answer :
    Method no.1: Convert the table into InnoDB will return the result faster. Allow to use OFFSET
    .@nb = query_sql("SELECT `name`, `kills`, FIND_IN_SET(`kills`, (SELECT GROUP_CONCAT(`kills` ORDER BY `kills` DESC) FROM `pvpladder`)) FROM `pvpladder` ORDER BY `kills` DESC LIMIT 5", .@name$, .@kills, .@rank); for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+ .@rank[.@i] +" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; Method no.2: This method return result faster than method 1 in huge table. Not allow to use OFFSET
    .@query$ = "SELECT `name`, IF(@d=t.`kills`, @r, @r:=@i), @d:=t.`kills`, @i:=@i+1 "; .@query$ += "FROM `pvpladder` t, (SELECT @d:=0, @r:=0, @i:=1)q "; .@query$ += "ORDER BY `kills` DESC LIMIT 5"; .@nb = query_sql(.@query$, .@name$, .@rank, .@kills, .@dummy); for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+ .@rank[.@i] +" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; .
    .
    Question : How do I show the current ranking of the player ?
    mes "Your kills -> "+ .@kills; mes "Your rank -> "+ .@rank; Answer :
    query_sql "SELECT `kills`, 1+(SELECT COUNT(1) FROM `pvpladder` t1 WHERE t1.`kills` > t2.`kills`) FROM `pvpladder` t2 WHERE `char_id` = "+ getcharid(0), .@kills, .@rank; Remember to index the `kills` field
     
    Reference : https://dba.stackexchange.com/questions/13703/get-the-rank-of-a-user-in-a-score-table
    .
     
  9. Upvote
    AnnieRuru got a reaction from wOni in Advance SQL commands   
    As usual, I only write advance guides
    This guide is a compilation of SQL commands that I have used, or Questions answered on the forum
    every single subject here are related to Hercules/Ragnarok Online in some ways, so you won't feel bored reading them XD
    Table of Content
    1. When to use *escape_sql script command
    2. How to build a case-sensitive table
    3. Choose a table type, MyISAM or InnoDB ?
    3a. How to index a table properly
    3b. Why you shouldn't use `char_reg_num_db` table
    4. AUTO_INCREMENT
    5. How to do IF-ELSE in SQL query ?
    5a. How to update multiple rows on different conditions in a single query
    6. How to show the current rank of the player
    7. INSERT INTO ... SELECT ...
    8. Table JOIN vs AS
    9. What is the maximum string limit for *query_sql
    9a. UNION
    This topic is now open to Suggestions, Ideas, Improvements, and Questions ~
    I'm sure many of you have some questions since the creation of this topic
    You may also post up your tricks if you want to share with us
  10. Upvote
    AnnieRuru got a reaction from hurtsky in Advance SQL commands   
    1. When to use escape_sql script command
     
    .
    input .@haha$; dispbottom .@haha$; dispbottom escape_sql(.@haha$); it doesn't has much differences, because it only affect 3 special characters
    ' <- single quotation mark
    " <- double quotation mark
    \ <- left slash
    if I input -> haha"lala'hehe <-
    it will return -> haha\"lala\'hehe <-
    this is what we call, Escape a character
    in hercules script, we also know we can use " symbol in any string input
    mes "Susan says :\" Today I ate 3 eggs \"."; where in the game client, you can see the " symbol in the npc msg box
    let's say I have a sql script like this
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input name, I'll show you all characters name it has on that player's account"; input .@name$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `name` LIKE '"+ .@name$ +"'", .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this script has a possibility to be hacked
    because to perform sql injection, I can enclose the string with quotation mark, then use another sql command to hack
    BUT with an escape_sql command, if the user want to enclose the string with quotation mark to hack the script
    the escape_sql command escaped the string, the quotation mark the user input will be escaped
    thus the script will become impossible to hack
    just now that script was for string input
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input account ID, I'll show you all characters name it has on that player's account"; input .@aid$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `account_id` = "+ escape_sql(.@aid$), .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this is another stupid case.
    1. the scripter use string input while the script just needed a number
    2. even with escape_sql command over there, there is no quotation mark at all
    yes this script also has a risk to be hack
    because escape_sql only escape quotation mark.
    that hacker don't even have to input quotation mark because it is a number
    and an injection query can be sent without any quotation mark input
    there are 2 ways to solve this
    either use numeric variable for the input command
    or enclose that ....
    ..... WHERE `account_id` = '"+ escape_sql(.@aid$) +"'", .... with single quotation mark, when the hacker input a quotation mark will be escaped by escape_sql command
    Reference : https://www.w3schools.com/sql/sql_injection.asp
    escape_sql command for another thing is
    if the player register their names containing ' or ", these characters are escaped
    only happens when the server have no restriction on the creation of players name
    hercules\conf\char\char-server.conf
    // Manage possible letters/symbol in the name of charater. Control character (0x00-0x1f) are never accepted. Possible values are: // NOTE: Applies to character, party and guild names. // 0: no restriction (default) // 1: only letters/symbols in 'name_letters' option. // 2: Letters/symbols in 'name_letters' option are forbidden. All others are possibles. name_option: 1  
    and this was what happened to my SQL dota pvpladder script
    Silo's Babies <-- this is a guild name
    you can see the 5th string has a single quotation mark
    with escape_sql command, that string will turn into
    Silo\'s Babies <-- the quotation mark is escaped when send to sql query
  11. Upvote
    AnnieRuru got a reaction from hurtsky in Advance SQL commands   
    2. How to build a case-sensitive table
    this is the answer I found
    http://dba.stackexchange.com/questions/15250/how-to-do-a-case-sensitive-search-in-where-clause
    by default, the table creation use charset = latin1;
    means it couldn't do a case-sensitive search
    if you want to do a case-sensitive in a query, use BINARY
    SELECT * FROM `char` WHERE `name` = BINARY('AnnieRuru'); however using BINARY might have performance hit if it is a big table
    so its more recommend to convert your SQL table to collate with latin1_general_cs

    let's say this is a sample table
    CREATE TABLE `test` ( `id` INT(11) PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(23) )ENGINE = InnoDB; do an ALTER table syntax
    ALTER TABLE `test` MODIFY COLUMN `name` VARCHAR(23) COLLATE latin1_general_cs; or just put it into the table creation
    CREATE TABLE `test` ( `id` INT(11) PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(23) )ENGINE = InnoDB DEFAULT CHARSET = latin1 COLLATE latin1_general_cs;  
  12. Upvote
    AnnieRuru got a reaction from hurtsky in Advance SQL commands   
    3. Choose a table type, MyISAM or InnoDB ?
    https://stackoverflow.com/questions/20148/myisam-versus-innodb
    Before MySQL 5.5,
    MyISAM is mostly use for read-heavy + table locking storage engine = such as pvp ladder ( always select ... order by kill desc )
    InnoDB is mostly use for write-heavy + row locking storage engine = such as quest script ( select ... from char_id ... only 1 row is retrieve )
    After MySQL 5.6, (currently is 8.0)
    just stick to InnoDB
    there is only 1 reason MyISAM is better than InnoDB
    - MyISAM use smaller disk usage than InnoDB
    let's take a look at our MyISAM to InnoDB converter
    https://github.com/HerculesWS/Hercules/blob/stable/sql-files/tools/convert_engine_innodb.sql
    This converter is useful if you are using MySQL 5.6 or above
    There are 4 tables that are commented out
    the reason is simple, these 4 tables only read once and forgotten when server is live
    since MyISAM is good at reading (SELECT) + smaller disk usage, its no use to convert these 4 tables into InnoDB
    3a How to index a table properly
    http://mysql.rjweb.org/doc.php/index_cookbook_mysql
    http://www.dbta.com/Columns/DBA-Corner/Top-10-Steps-to-Building-Useful-Database-Indexes-100498.aspx
    a simple thumb of rule, anything that is SELECT .... WHERE `field` = .....
    that `field` has to be index
    let's take a look at this PVP Ladder script that use Kill/Death ratio
    CREATE TABLE `pvpladder` ( `char_id` INT(11), `name` VARCHAR(23), `kills` INT(11), `death` INT(11), PRIMARY KEY (`char_id`), KEY (`kills`, `death`) ) ENGINE = InnoDB; prontera,155,186,6 script PVP Ladder 1_F_MARIA,{ .@nb = query_sql( "SELECT `name`, `kills`/(`death`+1) FROM `pvpladder` WHERE `kills` > 0 ORDER BY `kills`/(`death`+1) DESC LIMIT 10", .@name$, .@ratio$ ); if ( !.@nb ) { mes "no entry"; close; } mes "Current Ranking :"; for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+(.@i +1)+" ["+ .@name$[.@i] +"] "+ .@ratio$[.@i] +" kill"; close; OnPCKillEvent: if ( killedrid == getcharid(3) ) { // killing self should only increase death count. EG: Grand-cross query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 0,1 ) ON DUPLICATE KEY UPDATE `death` = `death` +1"; end; } query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 1,0 ) ON DUPLICATE KEY UPDATE `kills` = `kills` +1"; attachrid killedrid; query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 0,1 ) ON DUPLICATE KEY UPDATE `death` = `death` +1"; end; } This kind of query -> ORDER BY kills/death, needs to index them together like this
    KEY (`kills`, `death`) 3b. Why you shouldn't use `char_reg_num_db` table
    blame Euphy for spreading this technique
    There are 2 reasons why you shouldn't even touch all these variable tables
    Reason no.1 This table is sorely meant for server usage
    Once these data is loaded, it is process internally, and only save character data according to this configuration
    Reason no.2 The `value` field is not index !
    This line has ORDER BY `value`, try recheck our main.sql file
    CREATE TABLE IF NOT EXISTS `acc_reg_num_db` ( `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', `key` VARCHAR(32) BINARY NOT NULL DEFAULT '', `index` INT(11) UNSIGNED NOT NULL DEFAULT '0', `value` INT(11) NOT NULL DEFAULT '0', PRIMARY KEY (`account_id`,`key`,`index`), KEY `account_id` (`account_id`) ) ENGINE=MyISAM; SQL will search through every single line in the `value` field if that column isn't index
    Of course you can ... do ALTER table to add KEY to the `value` field
    but this table has already optimized in that way for server usage
    the more field you index into the table, the more disk usage space it use
    Conclusion : If you want to make a custom script, then make a custom table. Leave these table alone !
  13. Upvote
    AnnieRuru got a reaction from hurtsky in Advance SQL commands   
    4. AUTO_INCREMENT
    CREATE TABLE `support_ticket` ( `id` INT(11) AUTO_INCREMENT, `title` VARCHAR(70), `message` VARCHAR(255), PRIMARY KEY (`id`) ) ENGINE = InnoDB; In this kind of query that has AUTO_INCREMENT, many people do ....
    $support_ticket_id++; query_sql "INSERT INTO `support_ticket` VALUES ( "+ $support_ticket_id +", '"+ escape_sql(.@title$) ... can be optimize .... using NULL
    query_sql "INSERT INTO `support_ticket` VALUES ( NULL, '"+ escape_sql(.@title$) ... can retrieve the last row with
    query_sql "SELECT MAX(`id`) FROM `support_ticket`", .@id; // ----- OR ----- query_sql "SELECT LAST_INSERT_ID()", .@id;  
    Question : This question was asked on eathena forum board
    One of my friend touched my custom table and the AUTO_INCREMENT has jump off the value
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 25854 | <data set 4> | 25855 | <data set 5> | 25856 | <data set 6> I want to make the value return back to normal as shown
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 4 | <data set 4> | 5 | <data set 5> | 6 | <data set 6> How to do this WITHOUT losing any of the current data ?
    Answer: The trick is ... just drop that column and rebuild it
    ALTER TABLE `inventory` DROP COLUMN `id`; ALTER TABLE `inventory` ADD COLUMN `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; convert the table into MyISAM will process the query much faster
  14. Upvote
    AnnieRuru got a reaction from hurtsky in Advance SQL commands   
    5. How to do IF-ELSE in SQL query ?
    Question : I have a PVP ladder script that runs on Points system. Each kill plus 1 point and each death minus 1 point.
    The problem is, this query will make the points go into negative value if the player is being kill repeatedly
    query_sql "UPDATE `pvp_points` SET `points` = `points` - 1 WHERE `char_id` = "+ getcharid(0); How do I make the points stop at 0 if the player is already at 0 points ?
     
    Answer :
    query_sql "UPDATE `pvp_points` SET `points` = IF(`points` = 0, 0, `points` - 1) WHERE `char_id` = "+ getcharid(0); query_sql "UPDATE `pvp_points` SET `points` = (CASE WHEN `points` = 0 THEN 0 ELSE `points` - 1 END) WHERE `char_id` = "+ getcharid(0);  
    Explanations:
    similar to hercules script language,
    if (<condition>) <execute true condition>; else <execute false condition>; in SQL language
    IF(<condition>, <execute true condition>, <execute false condition>) CASE WHEN <condition> THEN <execute true condition> ELSE <execute false condition> END  
    Reference : https://www.w3schools.com/sql/func_mysql_if.asp
    https://stackoverflow.com/questions/63447/how-do-i-perform-an-if-then-in-an-sql-select
    5a. How to update multiple rows on different conditions in a single query
    This query will update multiple rows on different condition
    UPDATE `pvpladder` SET `points` = CASE WHEN `char_id` = 150000 THEN `points` +1 WHEN `char_id` = 150001 THEN `points` -1 END WHERE `char_id` IN (150000,150001); Reference : https://stackoverflow.com/questions/20255138/sql-update-multiple-records-in-one-query
  15. Upvote
    AnnieRuru got a reaction from Radian in Advance SQL commands   
    6. How to show the current rank of the player
    Question : This is the part of the script, output as below
    .@nb = query_sql("SELECT `name`, `kills` FROM `pvpladder` ORDER BY `kills` DESC LIMIT 5", .@name$, .@kills); for ( .@i = 0; .@i < .@nb; .@i++ ) mes "No."+(.@i+1)+" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; No.1 [Alice] ~ 19 kills No.2 [Emily] ~ 11 kills No.3 [Irene] ~ 11 kills No.4 [Brittany] ~ 8 kills No.5 [Fiona] ~ 7 kills  2nd place and 3rd place has the same amount of kills, how do I make both of them display as 2nd place like this ?
    No.1 [Alice] ~ 19 kills No.2 [Emily] ~ 11 kills No.2 [Irene] ~ 11 kills No.4 [Brittany] ~ 8 kills No.5 [Fiona] ~ 7 kills  
    Answer :
    Method no.1: Convert the table into InnoDB will return the result faster. Allow to use OFFSET
    .@nb = query_sql("SELECT `name`, `kills`, FIND_IN_SET(`kills`, (SELECT GROUP_CONCAT(`kills` ORDER BY `kills` DESC) FROM `pvpladder`)) FROM `pvpladder` ORDER BY `kills` DESC LIMIT 5", .@name$, .@kills, .@rank); for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+ .@rank[.@i] +" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; Method no.2: This method return result faster than method 1 in huge table. Not allow to use OFFSET
    .@query$ = "SELECT `name`, IF(@d=t.`kills`, @r, @r:=@i), @d:=t.`kills`, @i:=@i+1 "; .@query$ += "FROM `pvpladder` t, (SELECT @d:=0, @r:=0, @i:=1)q "; .@query$ += "ORDER BY `kills` DESC LIMIT 5"; .@nb = query_sql(.@query$, .@name$, .@rank, .@kills, .@dummy); for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+ .@rank[.@i] +" ["+ .@name$[.@i] +"] ~ "+ .@kills[.@i] +" kills"; .
    .
    Question : How do I show the current ranking of the player ?
    mes "Your kills -> "+ .@kills; mes "Your rank -> "+ .@rank; Answer :
    query_sql "SELECT `kills`, 1+(SELECT COUNT(1) FROM `pvpladder` t1 WHERE t1.`kills` > t2.`kills`) FROM `pvpladder` t2 WHERE `char_id` = "+ getcharid(0), .@kills, .@rank; Remember to index the `kills` field
     
    Reference : https://dba.stackexchange.com/questions/13703/get-the-rank-of-a-user-in-a-score-table
    .
     
  16. Upvote
    AnnieRuru got a reaction from IndieRO in Advance SQL commands   
    1. When to use escape_sql script command
     
    .
    input .@haha$; dispbottom .@haha$; dispbottom escape_sql(.@haha$); it doesn't has much differences, because it only affect 3 special characters
    ' <- single quotation mark
    " <- double quotation mark
    \ <- left slash
    if I input -> haha"lala'hehe <-
    it will return -> haha\"lala\'hehe <-
    this is what we call, Escape a character
    in hercules script, we also know we can use " symbol in any string input
    mes "Susan says :\" Today I ate 3 eggs \"."; where in the game client, you can see the " symbol in the npc msg box
    let's say I have a sql script like this
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input name, I'll show you all characters name it has on that player's account"; input .@name$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `name` LIKE '"+ .@name$ +"'", .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this script has a possibility to be hacked
    because to perform sql injection, I can enclose the string with quotation mark, then use another sql command to hack
    BUT with an escape_sql command, if the user want to enclose the string with quotation mark to hack the script
    the escape_sql command escaped the string, the quotation mark the user input will be escaped
    thus the script will become impossible to hack
    just now that script was for string input
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input account ID, I'll show you all characters name it has on that player's account"; input .@aid$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `account_id` = "+ escape_sql(.@aid$), .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this is another stupid case.
    1. the scripter use string input while the script just needed a number
    2. even with escape_sql command over there, there is no quotation mark at all
    yes this script also has a risk to be hack
    because escape_sql only escape quotation mark.
    that hacker don't even have to input quotation mark because it is a number
    and an injection query can be sent without any quotation mark input
    there are 2 ways to solve this
    either use numeric variable for the input command
    or enclose that ....
    ..... WHERE `account_id` = '"+ escape_sql(.@aid$) +"'", .... with single quotation mark, when the hacker input a quotation mark will be escaped by escape_sql command
    Reference : https://www.w3schools.com/sql/sql_injection.asp
    escape_sql command for another thing is
    if the player register their names containing ' or ", these characters are escaped
    only happens when the server have no restriction on the creation of players name
    hercules\conf\char\char-server.conf
    // Manage possible letters/symbol in the name of charater. Control character (0x00-0x1f) are never accepted. Possible values are: // NOTE: Applies to character, party and guild names. // 0: no restriction (default) // 1: only letters/symbols in 'name_letters' option. // 2: Letters/symbols in 'name_letters' option are forbidden. All others are possibles. name_option: 1  
    and this was what happened to my SQL dota pvpladder script
    Silo's Babies <-- this is a guild name
    you can see the 5th string has a single quotation mark
    with escape_sql command, that string will turn into
    Silo\'s Babies <-- the quotation mark is escaped when send to sql query
  17. Upvote
    AnnieRuru got a reaction from IndieRO in Advance SQL commands   
    2. How to build a case-sensitive table
    this is the answer I found
    http://dba.stackexchange.com/questions/15250/how-to-do-a-case-sensitive-search-in-where-clause
    by default, the table creation use charset = latin1;
    means it couldn't do a case-sensitive search
    if you want to do a case-sensitive in a query, use BINARY
    SELECT * FROM `char` WHERE `name` = BINARY('AnnieRuru'); however using BINARY might have performance hit if it is a big table
    so its more recommend to convert your SQL table to collate with latin1_general_cs

    let's say this is a sample table
    CREATE TABLE `test` ( `id` INT(11) PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(23) )ENGINE = InnoDB; do an ALTER table syntax
    ALTER TABLE `test` MODIFY COLUMN `name` VARCHAR(23) COLLATE latin1_general_cs; or just put it into the table creation
    CREATE TABLE `test` ( `id` INT(11) PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(23) )ENGINE = InnoDB DEFAULT CHARSET = latin1 COLLATE latin1_general_cs;  
  18. Upvote
    AnnieRuru got a reaction from IndieRO in Advance SQL commands   
    3. Choose a table type, MyISAM or InnoDB ?
    https://stackoverflow.com/questions/20148/myisam-versus-innodb
    Before MySQL 5.5,
    MyISAM is mostly use for read-heavy + table locking storage engine = such as pvp ladder ( always select ... order by kill desc )
    InnoDB is mostly use for write-heavy + row locking storage engine = such as quest script ( select ... from char_id ... only 1 row is retrieve )
    After MySQL 5.6, (currently is 8.0)
    just stick to InnoDB
    there is only 1 reason MyISAM is better than InnoDB
    - MyISAM use smaller disk usage than InnoDB
    let's take a look at our MyISAM to InnoDB converter
    https://github.com/HerculesWS/Hercules/blob/stable/sql-files/tools/convert_engine_innodb.sql
    This converter is useful if you are using MySQL 5.6 or above
    There are 4 tables that are commented out
    the reason is simple, these 4 tables only read once and forgotten when server is live
    since MyISAM is good at reading (SELECT) + smaller disk usage, its no use to convert these 4 tables into InnoDB
    3a How to index a table properly
    http://mysql.rjweb.org/doc.php/index_cookbook_mysql
    http://www.dbta.com/Columns/DBA-Corner/Top-10-Steps-to-Building-Useful-Database-Indexes-100498.aspx
    a simple thumb of rule, anything that is SELECT .... WHERE `field` = .....
    that `field` has to be index
    let's take a look at this PVP Ladder script that use Kill/Death ratio
    CREATE TABLE `pvpladder` ( `char_id` INT(11), `name` VARCHAR(23), `kills` INT(11), `death` INT(11), PRIMARY KEY (`char_id`), KEY (`kills`, `death`) ) ENGINE = InnoDB; prontera,155,186,6 script PVP Ladder 1_F_MARIA,{ .@nb = query_sql( "SELECT `name`, `kills`/(`death`+1) FROM `pvpladder` WHERE `kills` > 0 ORDER BY `kills`/(`death`+1) DESC LIMIT 10", .@name$, .@ratio$ ); if ( !.@nb ) { mes "no entry"; close; } mes "Current Ranking :"; for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+(.@i +1)+" ["+ .@name$[.@i] +"] "+ .@ratio$[.@i] +" kill"; close; OnPCKillEvent: if ( killedrid == getcharid(3) ) { // killing self should only increase death count. EG: Grand-cross query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 0,1 ) ON DUPLICATE KEY UPDATE `death` = `death` +1"; end; } query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 1,0 ) ON DUPLICATE KEY UPDATE `kills` = `kills` +1"; attachrid killedrid; query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 0,1 ) ON DUPLICATE KEY UPDATE `death` = `death` +1"; end; } This kind of query -> ORDER BY kills/death, needs to index them together like this
    KEY (`kills`, `death`) 3b. Why you shouldn't use `char_reg_num_db` table
    blame Euphy for spreading this technique
    There are 2 reasons why you shouldn't even touch all these variable tables
    Reason no.1 This table is sorely meant for server usage
    Once these data is loaded, it is process internally, and only save character data according to this configuration
    Reason no.2 The `value` field is not index !
    This line has ORDER BY `value`, try recheck our main.sql file
    CREATE TABLE IF NOT EXISTS `acc_reg_num_db` ( `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', `key` VARCHAR(32) BINARY NOT NULL DEFAULT '', `index` INT(11) UNSIGNED NOT NULL DEFAULT '0', `value` INT(11) NOT NULL DEFAULT '0', PRIMARY KEY (`account_id`,`key`,`index`), KEY `account_id` (`account_id`) ) ENGINE=MyISAM; SQL will search through every single line in the `value` field if that column isn't index
    Of course you can ... do ALTER table to add KEY to the `value` field
    but this table has already optimized in that way for server usage
    the more field you index into the table, the more disk usage space it use
    Conclusion : If you want to make a custom script, then make a custom table. Leave these table alone !
  19. Upvote
    AnnieRuru got a reaction from IndieRO in Advance SQL commands   
    4. AUTO_INCREMENT
    CREATE TABLE `support_ticket` ( `id` INT(11) AUTO_INCREMENT, `title` VARCHAR(70), `message` VARCHAR(255), PRIMARY KEY (`id`) ) ENGINE = InnoDB; In this kind of query that has AUTO_INCREMENT, many people do ....
    $support_ticket_id++; query_sql "INSERT INTO `support_ticket` VALUES ( "+ $support_ticket_id +", '"+ escape_sql(.@title$) ... can be optimize .... using NULL
    query_sql "INSERT INTO `support_ticket` VALUES ( NULL, '"+ escape_sql(.@title$) ... can retrieve the last row with
    query_sql "SELECT MAX(`id`) FROM `support_ticket`", .@id; // ----- OR ----- query_sql "SELECT LAST_INSERT_ID()", .@id;  
    Question : This question was asked on eathena forum board
    One of my friend touched my custom table and the AUTO_INCREMENT has jump off the value
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 25854 | <data set 4> | 25855 | <data set 5> | 25856 | <data set 6> I want to make the value return back to normal as shown
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 4 | <data set 4> | 5 | <data set 5> | 6 | <data set 6> How to do this WITHOUT losing any of the current data ?
    Answer: The trick is ... just drop that column and rebuild it
    ALTER TABLE `inventory` DROP COLUMN `id`; ALTER TABLE `inventory` ADD COLUMN `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; convert the table into MyISAM will process the query much faster
  20. Upvote
    AnnieRuru got a reaction from IndieRO in Advance SQL commands   
    5. How to do IF-ELSE in SQL query ?
    Question : I have a PVP ladder script that runs on Points system. Each kill plus 1 point and each death minus 1 point.
    The problem is, this query will make the points go into negative value if the player is being kill repeatedly
    query_sql "UPDATE `pvp_points` SET `points` = `points` - 1 WHERE `char_id` = "+ getcharid(0); How do I make the points stop at 0 if the player is already at 0 points ?
     
    Answer :
    query_sql "UPDATE `pvp_points` SET `points` = IF(`points` = 0, 0, `points` - 1) WHERE `char_id` = "+ getcharid(0); query_sql "UPDATE `pvp_points` SET `points` = (CASE WHEN `points` = 0 THEN 0 ELSE `points` - 1 END) WHERE `char_id` = "+ getcharid(0);  
    Explanations:
    similar to hercules script language,
    if (<condition>) <execute true condition>; else <execute false condition>; in SQL language
    IF(<condition>, <execute true condition>, <execute false condition>) CASE WHEN <condition> THEN <execute true condition> ELSE <execute false condition> END  
    Reference : https://www.w3schools.com/sql/func_mysql_if.asp
    https://stackoverflow.com/questions/63447/how-do-i-perform-an-if-then-in-an-sql-select
    5a. How to update multiple rows on different conditions in a single query
    This query will update multiple rows on different condition
    UPDATE `pvpladder` SET `points` = CASE WHEN `char_id` = 150000 THEN `points` +1 WHEN `char_id` = 150001 THEN `points` -1 END WHERE `char_id` IN (150000,150001); Reference : https://stackoverflow.com/questions/20255138/sql-update-multiple-records-in-one-query
  21. Upvote
    AnnieRuru got a reaction from bWolfie in Advance SQL commands   
    5. How to do IF-ELSE in SQL query ?
    Question : I have a PVP ladder script that runs on Points system. Each kill plus 1 point and each death minus 1 point.
    The problem is, this query will make the points go into negative value if the player is being kill repeatedly
    query_sql "UPDATE `pvp_points` SET `points` = `points` - 1 WHERE `char_id` = "+ getcharid(0); How do I make the points stop at 0 if the player is already at 0 points ?
     
    Answer :
    query_sql "UPDATE `pvp_points` SET `points` = IF(`points` = 0, 0, `points` - 1) WHERE `char_id` = "+ getcharid(0); query_sql "UPDATE `pvp_points` SET `points` = (CASE WHEN `points` = 0 THEN 0 ELSE `points` - 1 END) WHERE `char_id` = "+ getcharid(0);  
    Explanations:
    similar to hercules script language,
    if (<condition>) <execute true condition>; else <execute false condition>; in SQL language
    IF(<condition>, <execute true condition>, <execute false condition>) CASE WHEN <condition> THEN <execute true condition> ELSE <execute false condition> END  
    Reference : https://www.w3schools.com/sql/func_mysql_if.asp
    https://stackoverflow.com/questions/63447/how-do-i-perform-an-if-then-in-an-sql-select
    5a. How to update multiple rows on different conditions in a single query
    This query will update multiple rows on different condition
    UPDATE `pvpladder` SET `points` = CASE WHEN `char_id` = 150000 THEN `points` +1 WHEN `char_id` = 150001 THEN `points` -1 END WHERE `char_id` IN (150000,150001); Reference : https://stackoverflow.com/questions/20255138/sql-update-multiple-records-in-one-query
  22. Upvote
    AnnieRuru got a reaction from Quazi in Advance SQL commands   
    4. AUTO_INCREMENT
    CREATE TABLE `support_ticket` ( `id` INT(11) AUTO_INCREMENT, `title` VARCHAR(70), `message` VARCHAR(255), PRIMARY KEY (`id`) ) ENGINE = InnoDB; In this kind of query that has AUTO_INCREMENT, many people do ....
    $support_ticket_id++; query_sql "INSERT INTO `support_ticket` VALUES ( "+ $support_ticket_id +", '"+ escape_sql(.@title$) ... can be optimize .... using NULL
    query_sql "INSERT INTO `support_ticket` VALUES ( NULL, '"+ escape_sql(.@title$) ... can retrieve the last row with
    query_sql "SELECT MAX(`id`) FROM `support_ticket`", .@id; // ----- OR ----- query_sql "SELECT LAST_INSERT_ID()", .@id;  
    Question : This question was asked on eathena forum board
    One of my friend touched my custom table and the AUTO_INCREMENT has jump off the value
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 25854 | <data set 4> | 25855 | <data set 5> | 25856 | <data set 6> I want to make the value return back to normal as shown
    | 1 | <data set 1> | 2 | <data set 2> | 3 | <data set 3> | 4 | <data set 4> | 5 | <data set 5> | 6 | <data set 6> How to do this WITHOUT losing any of the current data ?
    Answer: The trick is ... just drop that column and rebuild it
    ALTER TABLE `inventory` DROP COLUMN `id`; ALTER TABLE `inventory` ADD COLUMN `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST; convert the table into MyISAM will process the query much faster
  23. Upvote
    AnnieRuru got a reaction from Quazi in Advance SQL commands   
    1. When to use escape_sql script command
     
    .
    input .@haha$; dispbottom .@haha$; dispbottom escape_sql(.@haha$); it doesn't has much differences, because it only affect 3 special characters
    ' <- single quotation mark
    " <- double quotation mark
    \ <- left slash
    if I input -> haha"lala'hehe <-
    it will return -> haha\"lala\'hehe <-
    this is what we call, Escape a character
    in hercules script, we also know we can use " symbol in any string input
    mes "Susan says :\" Today I ate 3 eggs \"."; where in the game client, you can see the " symbol in the npc msg box
    let's say I have a sql script like this
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input name, I'll show you all characters name it has on that player's account"; input .@name$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `name` LIKE '"+ .@name$ +"'", .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this script has a possibility to be hacked
    because to perform sql injection, I can enclose the string with quotation mark, then use another sql command to hack
    BUT with an escape_sql command, if the user want to enclose the string with quotation mark to hack the script
    the escape_sql command escaped the string, the quotation mark the user input will be escaped
    thus the script will become impossible to hack
    just now that script was for string input
    prontera,153,171,5 script Show Characters 1_F_MARIA,{ mes "input account ID, I'll show you all characters name it has on that player's account"; input .@aid$; .@nb = query_sql("SELECT `char_id`, `name` FROM `char` WHERE `account_id` = "+ escape_sql(.@aid$), .@cid, .@name$); if ( !.@nb ) { mes "no result"; close; } for ( .@i = 0; .@i < .@nb; ++.@i ) mes .@cid[.@i] +" "+ .@name$[.@i]; close; } this is another stupid case.
    1. the scripter use string input while the script just needed a number
    2. even with escape_sql command over there, there is no quotation mark at all
    yes this script also has a risk to be hack
    because escape_sql only escape quotation mark.
    that hacker don't even have to input quotation mark because it is a number
    and an injection query can be sent without any quotation mark input
    there are 2 ways to solve this
    either use numeric variable for the input command
    or enclose that ....
    ..... WHERE `account_id` = '"+ escape_sql(.@aid$) +"'", .... with single quotation mark, when the hacker input a quotation mark will be escaped by escape_sql command
    Reference : https://www.w3schools.com/sql/sql_injection.asp
    escape_sql command for another thing is
    if the player register their names containing ' or ", these characters are escaped
    only happens when the server have no restriction on the creation of players name
    hercules\conf\char\char-server.conf
    // Manage possible letters/symbol in the name of charater. Control character (0x00-0x1f) are never accepted. Possible values are: // NOTE: Applies to character, party and guild names. // 0: no restriction (default) // 1: only letters/symbols in 'name_letters' option. // 2: Letters/symbols in 'name_letters' option are forbidden. All others are possibles. name_option: 1  
    and this was what happened to my SQL dota pvpladder script
    Silo's Babies <-- this is a guild name
    you can see the 5th string has a single quotation mark
    with escape_sql command, that string will turn into
    Silo\'s Babies <-- the quotation mark is escaped when send to sql query
  24. Upvote
    AnnieRuru got a reaction from Quazi in Advance SQL commands   
    2. How to build a case-sensitive table
    this is the answer I found
    http://dba.stackexchange.com/questions/15250/how-to-do-a-case-sensitive-search-in-where-clause
    by default, the table creation use charset = latin1;
    means it couldn't do a case-sensitive search
    if you want to do a case-sensitive in a query, use BINARY
    SELECT * FROM `char` WHERE `name` = BINARY('AnnieRuru'); however using BINARY might have performance hit if it is a big table
    so its more recommend to convert your SQL table to collate with latin1_general_cs

    let's say this is a sample table
    CREATE TABLE `test` ( `id` INT(11) PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(23) )ENGINE = InnoDB; do an ALTER table syntax
    ALTER TABLE `test` MODIFY COLUMN `name` VARCHAR(23) COLLATE latin1_general_cs; or just put it into the table creation
    CREATE TABLE `test` ( `id` INT(11) PRIMARY KEY AUTO_INCREMENT, `name` VARCHAR(23) )ENGINE = InnoDB DEFAULT CHARSET = latin1 COLLATE latin1_general_cs;  
  25. Upvote
    AnnieRuru got a reaction from Quazi in Advance SQL commands   
    3. Choose a table type, MyISAM or InnoDB ?
    https://stackoverflow.com/questions/20148/myisam-versus-innodb
    Before MySQL 5.5,
    MyISAM is mostly use for read-heavy + table locking storage engine = such as pvp ladder ( always select ... order by kill desc )
    InnoDB is mostly use for write-heavy + row locking storage engine = such as quest script ( select ... from char_id ... only 1 row is retrieve )
    After MySQL 5.6, (currently is 8.0)
    just stick to InnoDB
    there is only 1 reason MyISAM is better than InnoDB
    - MyISAM use smaller disk usage than InnoDB
    let's take a look at our MyISAM to InnoDB converter
    https://github.com/HerculesWS/Hercules/blob/stable/sql-files/tools/convert_engine_innodb.sql
    This converter is useful if you are using MySQL 5.6 or above
    There are 4 tables that are commented out
    the reason is simple, these 4 tables only read once and forgotten when server is live
    since MyISAM is good at reading (SELECT) + smaller disk usage, its no use to convert these 4 tables into InnoDB
    3a How to index a table properly
    http://mysql.rjweb.org/doc.php/index_cookbook_mysql
    http://www.dbta.com/Columns/DBA-Corner/Top-10-Steps-to-Building-Useful-Database-Indexes-100498.aspx
    a simple thumb of rule, anything that is SELECT .... WHERE `field` = .....
    that `field` has to be index
    let's take a look at this PVP Ladder script that use Kill/Death ratio
    CREATE TABLE `pvpladder` ( `char_id` INT(11), `name` VARCHAR(23), `kills` INT(11), `death` INT(11), PRIMARY KEY (`char_id`), KEY (`kills`, `death`) ) ENGINE = InnoDB; prontera,155,186,6 script PVP Ladder 1_F_MARIA,{ .@nb = query_sql( "SELECT `name`, `kills`/(`death`+1) FROM `pvpladder` WHERE `kills` > 0 ORDER BY `kills`/(`death`+1) DESC LIMIT 10", .@name$, .@ratio$ ); if ( !.@nb ) { mes "no entry"; close; } mes "Current Ranking :"; for ( .@i = 0; .@i < .@nb; ++.@i ) mes "No."+(.@i +1)+" ["+ .@name$[.@i] +"] "+ .@ratio$[.@i] +" kill"; close; OnPCKillEvent: if ( killedrid == getcharid(3) ) { // killing self should only increase death count. EG: Grand-cross query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 0,1 ) ON DUPLICATE KEY UPDATE `death` = `death` +1"; end; } query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 1,0 ) ON DUPLICATE KEY UPDATE `kills` = `kills` +1"; attachrid killedrid; query_sql "INSERT INTO `pvpladder` VALUES ( "+ getcharid(0) +", '"+ escape_sql( strcharinfo(0) )+"', 0,1 ) ON DUPLICATE KEY UPDATE `death` = `death` +1"; end; } This kind of query -> ORDER BY kills/death, needs to index them together like this
    KEY (`kills`, `death`) 3b. Why you shouldn't use `char_reg_num_db` table
    blame Euphy for spreading this technique
    There are 2 reasons why you shouldn't even touch all these variable tables
    Reason no.1 This table is sorely meant for server usage
    Once these data is loaded, it is process internally, and only save character data according to this configuration
    Reason no.2 The `value` field is not index !
    This line has ORDER BY `value`, try recheck our main.sql file
    CREATE TABLE IF NOT EXISTS `acc_reg_num_db` ( `account_id` INT(11) UNSIGNED NOT NULL DEFAULT '0', `key` VARCHAR(32) BINARY NOT NULL DEFAULT '', `index` INT(11) UNSIGNED NOT NULL DEFAULT '0', `value` INT(11) NOT NULL DEFAULT '0', PRIMARY KEY (`account_id`,`key`,`index`), KEY `account_id` (`account_id`) ) ENGINE=MyISAM; SQL will search through every single line in the `value` field if that column isn't index
    Of course you can ... do ALTER table to add KEY to the `value` field
    but this table has already optimized in that way for server usage
    the more field you index into the table, the more disk usage space it use
    Conclusion : If you want to make a custom script, then make a custom table. Leave these table alone !
×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.