Saturday, November 7, 2020

Linux compiler error that never shown on Visual Studio

let's just admit Visual Studio sux ... ok ?

right now, if you want to host a Private Ragnarok Online server, its just better to choose Debian or CentOS option
the reason is because if you choose Windows Server Edition on VPS,
the cost of running on Windows is often double or triple compare to Linux environment

because of this, programmer like me who write and compile their plugin/source code with Visual Studio,
it will work fine on Windows environment.
But when apply the code on Live server (Linux environment), it will throw non-sensible error

No.1
warning: implicit declaration of function ‘atoi’ [-Wimplicit-function-declaration]

Avoid using atoi like plague !!
in npc scripting its the only script command to convert string into int
yes, this actually working fine with Visual Studio, but on Linux ...



No.2
error: a label can only be part of a statement and a declaration is not a statement

Sample



this case have to be enclose with curly bracket, like this


No.3
warning: unused variable ‘len’ [-Wunused-variable]

Sample
this is very annoying, because I want to output the value from 4th argument, but the 2nd argument isn't use
on Windows this is fine, but on Linux it throws that error throw off my guard

change


there might be more, will keep this update

Monday, November 2, 2020

Page by page

*mes

sometimes you want to display a long long array list of objects,
for example print out all values from a table
this will throw infinite loop ERROR

the reason is because the script engine has some protection against infinite loop check
conf\map\script.conf
even if you put a *freeloop there, it might still lag the server

in this case, its better to just display page by page


if the *query_sql still cause lag, have to use OFFSET


and this is with page input for your amusement :)



*select

same as above this will throw infinity loop ERROR
even if you put a LIMIT syntax this will throw menu length too long / too many options ERROR

this is because the maximum limit of menu length in string (eg: *getstrlen) is 2047
and the maximum limit of menu options is 254
src\map\script.h
and I don't recommend changing this, as you also have to change the packet structure etc ...

so the only way to remedy this ... is again ... page by page

if the query_sql still cause lag, have to use OFFSET


and the page select display for your amusement :)


External Links:
Help - Array in menu

Wednesday, October 14, 2020

How to send Weekly Rewards to players

now I have this script I want to send the weekly reward to players by mail, here's my take on how to tackle this problem yup, basically I just added a few more line down there

now, this is what happen when the script first loaded

1. when the server load the script for the first time, it will execute OnInit: and try to save the date of Sunday on that week

  • CURDATE() means today is '20201014'
  • DAYOFWEEK(CURDATE()) means today is Wednesday(4) -> Sunday is (1)
  •  that's why needs a -1 , DAYOFWEEK(CURDATE())-1 means -3
  • SUBDATE(CURDATE(), INTERVAL (DAYOFWEEK(CURDATE()) -1) DAY)
    • SUBDATE('20201014', INTERVAL (3) DAY)
    • return '2020-10-11' is the Sunday of that week
  •  since it return '2020-10-11', have to kill the '-' symbol, use REPLACE syntax to remove that symbol

this value '20201011' is save into $poring_rank_1st_sunday and remain unchange forever
and the script will always try to compare to this value in DATEDIFF

now, this is what happen on the next week when triggering OnSun0000: label
"SELECT DATEDIFF(CURDATE(), '"+ $poring_rank_1st_sunday +"')/7"

  • CURDATE() is '20201018', the Sunday on the next week
  • DATEDIFF('20201018', '20201011') return 7, since we need to compare in weeks, do a /7, 7/7 = 1
  • .this_week_index = 1, $poring_rank_week_index = 0, the value are different now, thus it will try to reset the ladder

and once the ladder is reset, set $poring_rank_week_index(0) into .this_week_index(1) so it won't trigger on OnInit: label again
even doing a @reloadscript, it will compare the week index as 1 == 1, and won't execute ladder reset
only the next week again, compare the week index as 2 == 1, only will execute ladder reset

unlike the monthly reset, which save the actual date,
this one has to increase the week index counter even if there are no ranking in that week

External Links:
Weekly MVP Rewards

See Also:
How to send Monthly Rewards to players

How to send Monthly Reward to players

now I have this script I want to send the monthly reward to players by mail, here's my take on how to tackle this problem
yup, basically I just added a few more line down there

now break it into 3 parts

  1. when the server first load the script, it will execute OnInit: and store the $poringkill_last_given variable
    it will store the value as '202010', 2020 is the year, and 10 is the current month
  2.  then on 12:00am, it will execute OnClock0000: label,
    with the if (gettime(GETTIME_DAYOFMONTH) != 1) end; to halt the script if it isn't the day 1 of the month
    since it is a new month, '202011' is different from '202010' and thus execute the condition below
  3. The second condition of OnInit: label is to make sure it will run L_GiveMonthlyReward: label IF the server isn't online during 12:00am of day 1 of the month.
    It will compare to the $poringkill_last_given variable every time, if it is different month, then it will execute the statement below
    it has to bypass the if (gettime(GETTIME_DAYOFMONTH) != 1) end; so even if the server somehow didn't even online on day 1 of the month, it will still giving out monthly reward for previous month

External Link:
Monthly MVP Rank

See Also:
How to send Weekly Rewards to players

Thursday, October 8, 2020

compare regular expression on rAthena & Hercules

The most famous script that uses Regular Expression is GmOcean's Disguise Event
to learn more about Regular Expression, refer to Wikipedia
Visit this site for more in-depth tutorial

This NPC will Copy whatever you type
It's common practice that you should include ^ at the beginning and $ at the end
without it, players can input whitespace to bypass the check

Example:
(.@a$ ~= "asdf") = it can be "asdf", "aaasdf", "asdfffff", " asdf   " and so on
(.@a$ ~= "^asdf") = it can be "asdf", "asdffffff", "asdfasdf", "asdf   " <-- this just check the begining string
(.@a$ ~= "^asdf$") = it can only be "asdf", no other match

defpattern 1, "([^:]+): asdf","L_0"; <-- it can be "asdf", "asdfffffff", "asdfasdf", "asdf    " and so on
defpattern 1, "([^:]+): asdf$","L_0"; <-- only "asdf" will trigger the label L_0, OR "ASDF", "AsDf" and so on ...

yup, *pcre_match is case-sensitive, but *defpattern isn't, remember to keep this in mind
to make *pcre_match case-insensitive, use (?i) flag
to make *defpattern case-sensitive, use (?-i) flag


in practice, this command is most useful to check invalid character input
- only allow 0-9, a-z, A-Z or follow a format, like MySQL datetime format

for rAthena counterpart, rAthena has *preg_match instead, which follow PHP style input
However unlike Hercules, there are no way to capture the strings within a bracket
rAthena's *preg_match can only returns 0 or 1
correct me if I'm wrong, the source code seems to indicate it can return 0~10 but I couldn't get it display 2 or above



Some older client that diff with multi-language support will return |00 in front of character speech
try check it with *consolemes with the very first script (rAthena is *debugmes)
if that happens, your syntax has to change into


External Links:
Disguise Event fix
defpattern regexp
ignore case sensitive in regexp

Saturday, October 3, 2020

*input with optional min/max parameter

I still see a lot people do this

although this isn't bad by any means, but we can hijack it with *input <var>,<min>,<max>;


yup, just 1 line, do all 3 conditions above

No. 1 - *input script command can return -1,0,1 these 3 values
if the input amount is smaller than <min>, then it will return with the condition -1
since we don't want to close; the script on condition 0 or 1, then put the <min> as 1,
when player input as 0, it will return as condition -1

No. 2 - The <max> can insert with the condition countitem(Poring_Coin), or 100, depends on the script
Or ... just do both by having a *min script command <-- pick the lowest value in the group

DO NOT UNDERESTIMATE THIS TRICK
This is extremely useful if there is a complex +/- calculation involve
Example this custom bank script
Player only needs to input 9999999999 and it will automatically convert into accepted value without having to pull out a calculator

External Links:
Zeny in separate table

Monday, September 28, 2020

moving word in waitingroom

today I just review another member's script and this part caught my eye

HAHAHA !! this is so cute yet so wasting server resources !!!


I dunno I should praise him for creativity or comment him for being dump for wasting so much server bandwidth for doing this 😅

yes I don't recommend doing this on your live server ...
because every *waitingroom script command means sending one packet to each player surround this npc
... but I'm sooo impressed 😝

Sunday, September 27, 2020

A race condition in npc scripts

today I just reviewed a paid script (I couldn't show because its paid)
and I saw multiple errors and mistakes on the said script !!

this one in particular, is something less well known, but it is VERY important if you are making a Game script

let's give an example

at first glance this script looks bug free, but there is a race condition going on after the *input command

step to reproduce :-
1. open up 4 client, first 2 player claim the reward normally
2. on 3rd client, type 'ragnarok' but don't hit enter yet
3. on 4th client, type 'ragnarok' and hit enter, now the script says the event has ended
4. back on 3rd client, hit the Enter key, now this event has 4 players won this event !

to prevent this from happening, needs to add another check right after the *input command


another example is on my Private MVP Room script, which can be found in rathena repo
https://github.com/rathena/rathena/blob/a4d57cb8a398368c0d720aedc669dff88f73c4d4/npc/custom/etc/mvp_room.txt#L62-L66

now try remove these line
1. let player A register an empty room, but left the menu window open
2. player B register the empty room faster than player A
3. now both player A and player B can go into the same room !
4. if this is register under party/guild, player's B party/guild member couldn't get in even after paying the fees !!

so remember, if you are making a Game or Event script, every time after hitting a next; *menu *select *input *progressbar
the value might have changed from the original value, and might lead to race conditions between players

The Importance of *checkweight

today I just reviewed a paid script (I couldn't show because its paid)
and I saw multiple errors and mistakes on the said script !!

and this is one that MANY ... I mean MANY scripters has made this mistakes !!


if the player is currently overweight, or having more items than *getinventorysize(), the item will drop on the floor
if this actually happens, other players around this npc can come in to steal the reward item while the actual winner still desperately trying to store item

to fix this, all you have to do is add *checkweight script command
or use *rodex_sendmail script command ... or *mail script command for rathena

Friday, February 22, 2019

List pet eggs in your inventory

Just like signed items, which use 255 or 254 in the card1 field


Hercules Pet egg uses -256 in the card1 field
rathena use 256 ... without the negative

the earliest ... traceable topic is over here
https://rathena.org/board/topic/76954-can-i-request-script-evolution-pet/?sortby=date

and now for the latest script available...
https://drive.google.com/file/d/1hQgJO4MbHIQtymKxnsrxsi37nkJ1eruz/view

the way to retrieve the pet ID is almost similar to the signed item

Signed Item
card1 = 255 or 254
card2 = star crumb + element
card3 = CHAR_ID & (( 1 << 16 ) -1)
card4 = CHAR_ID >> 16

Pet ID
card1 = -256 (Hercules) or 256 (rAthena)
card2 = CHAR_ID & (( 1 << 16 ) -1)
card3 = CHAR_ID >> 16
card4 = 0


External Links:

Pet egg renewal fixing

Saturday, February 16, 2019

Hercules script engine is case-sensitive, rAthena isn't

I still remember when I was still Scripting Moderator in rAthena, there was this old bug report ...
...  after rAthena switched to IPB4 ... forum bug tracker down ....
anyway, it goes like this

Joseph open a bug report, claiming some official script doesn't execute the monster label properly
after some debate between the developers, I posted a script and prove them it is true
make one npc with the capital letter, and another one without capital letter -> OnDead - Ondead
click on 'asdf' npc, after kill the monster, the map-server.exe spam error
BrianL immediately add tag [Confirmed] and [Severity-Medium] to this bug report
within a few weeks, both Euphy and Joseph fix all the npc scripts in the emulator, and they mark it as [Solved]

..... the truth is, if you try the above script in latest rAthena, this bug still happen TODAY
this bug can't reproduce in Hercules because Hercules has case-sensitive script engine



Now let's talk about how crazy the script can be, if the script engine is not case-sensitive

other than npc header, 'prontera' and '1_F_MARIA',
script commands, labels, variables and constants are case-insensitive in rAthena
strings that enclosed in " double-quotation mark are not affected
means "this" "--ja--" "all" "All" still has to retain its format


This will throw duplicate label error in rAthena
in Hercules, click on the npc will display "2" properly

So, rAthena users, remember, don't simply use Onmobdead: label in your scripts
because official script already uses OnMobDead, OnMyMobDead, ... etc
... anyways, any official script already using that label name, Don't change into small letters in your scripts


... btw, SQL commands are still case-insensitive,
so me as Hercules user still uses small letters in *query_sql script commands


External Links:

Player Checker NPC
... once rAthena forum bug tracker up, will post that bug tracker ID here ...

Friday, February 15, 2019

messagecolor

make the NPC or monster object display chat-box in colorize message


Download :

Hercules Plugin
rAthena Patch

Note: this 0.3 version can display chat-box for the player
however the chat-box above your head isn't colorize because there are no known way to do it

Hercules' @fontcolor actually use a trick to disguise the player
https://github.com/HerculesWS/Hercules/issues/1217
https://github.com/HerculesWS/Hercules/issues/1930
and it has some bugs, so its better not to emulate that pattern
.... apparently rAthena @fontcolor is broken ...

Saturday, February 9, 2019

What is dynamic_mob and how it affects scripts

What is dynamic_mob ?

conf/map/battle/monster.conf

This setting means, monsters only spawn on the map when there is a player warp in.
if the map is left empty for 5 minutes, all dynamic spawned monsters, except spawn by boss_monster will remove from the map

it is recommend to turn on dynamic_mob because it help save memory on your live server

Example:

Let's say you login in Prontera town, and type "@asdf", poring and golden_bug are not spawn, only poporing has been spawn
"@rura guild_vs2", warp into the map, all 3 monsters are spawned
"@go 0", return to prontera, after 5 minutes, the poring is remove from the map, only golden_bug and poporing remain
"@reloadscript", return everything as normal, only poporing remain

This means :-
1. All monster spawn dynamically are affected by dynamic_mob, *monster script command are not affected
2. once a player warp into the map, all monsters will spawn normally
3. after 5 minutes the map left empty, all non-boss_monster will be remove

This can cause problem to the script if you want to count how many monsters left in the map
because dynamic_mob will NOT load the monster until a player warp into the map


External Links:

mobs on server start
MVP board

Wednesday, February 6, 2019

Allow OnNPCKillEvent to increase kill counter for party members

I still see a lot members struggle with this issue,
however this is entirely done with scripting alone

this is a normal template to increase kill counter normally for the killer alone

and this is for party support


Now to break it down part by part



this part is obvious, prevent *getpartymember throw error when the player doesn't own a party



change the scope of the killedrid from player based to script state
so there is no need to use *getvariableofpc (hercules) or *getvar (rAthena)


copy paste this part from script commands.txt

// loop through both and use 'isloggedin' to count online party members
    for (.@i = 0; .@i < $@partymembercount; ++.@i)
        if (isloggedin($@partymemberaid[.@i], $@partymembercid[.@i]))
            .@count_online++;
// We search accountID & charID because a single party can have
// multiple characters from the same account. Without searching
// through the charID, if a player has 2 characters from the same
// account inside the party but only 1 char online, it would count
// their online char twice.



the first 2 conditions is to check the party members are within range of the killer
means if the attached party member having same map with the killer, and within 30 cells of the killer

Why 30 ?? this is actually a magical number
you see, because the OnNPCKillEvent return the mob ID (1002) instead of game ID (11000021)
we can't really use mob controller system to retrieve the coordinate of the monster
so we use the range from the killer instead
I have actually tried to use getbattleflag("area_size") before, but players complained it doesn't work
so I increased the distance to 30 (about twice the length), and they say the script is fixed
weird ... I know, so the number 30 is kinda stuck in most of my scripts

1 possible explanation is this

if the Archer shoot long distances, like using a bow, throw arrow, or mage firebolt, crusader throw shield
then the party member from across the screen should gets the kill
"area_size" is 14, so 14+14 = 28, so 30 works for them


Hp ... you don't want the counter to increase for dead party member
so if that player is dead, Hp is 0, and the condition will become false there
this is to prevent having players increase the kill count by just AFK there


External Links:

Euphy's Hunting Mission ( check this line )
Party Share Kill ( tr0n's Quest Board )

Regular Expression Replace in Notepad++

Notepad++ can search strings and replace them using regular expression
Regular_Expression (Wikipedia)
it can be use to :-
1. generate a huge list of mapflags
2. fix outdated format on the script commands
3. apply a quick fix on CVS style database
4. convert client side item list into array




Example 1. Generate a huge list of mapflags


1. goto db/map_index.txt, copy over the map list

2. there are some lines commented, so find the line with // and remove them by doing this
Find with regular expression Replace with empty string, this effectively leave the line blank

3. Edit -> Line Operations -> Remove Empty lines

4. Find with regular expression Replace with


Example 2. Fix outdated format on the script commands


Find with regular expression Replace with

Example 3. Apply a quick fix on CVS style database

rathena only, Hercules has switched to libconfig format
goto db/re/item_db.txt

Find with regular expression Replace with Find with regular expression Replace with Find with regular expression Replace with


Example 4: Convert client side item list into array

goto kro/data/cardprefixnametable.txt

Find with regular expression Replace with


External Links:

Disable dead branch (Example 1) 
Convert pow(a,b) into a**b (Example 2)
Swap item type IT_ARMOR and IT_WEAPON (Example 3)
getitemname2 function (Example 4)

Saturday, February 2, 2019

One Character per GM account

Download:

Hercules Patch
Hercules Plugin

Very simple trick, only allow them to create/login their character on slot 0

if you want to make them limit to 3 characters, then allow them to only use slot 0,1,2

 

External Links:

One char per GM Account

Friday, February 1, 2019

OnPCStatCalcEvent

Let's have a little history lesson shall we ?

it all started by me, found this modification in this topic
about update character's status


since then I have been using it VERY frequently
I'm sure those of you dealing with patch understand this,
I have to upkeep the patch to the latest server revision
eventually I was so fed up, and finally brought it to the development suggestion
See -> OnPCStatCalcEvent <- that's how rAthena got it

When I switch to Hercules, somebody else actually made a pull request on it
https://github.com/HerculesWS/Hercules/pull/351
but it was denied, and thus I released it as a plugin

For a long period of time, the *recalculatestat script command is still broken,
but I finally fix it during my time in Hercules period

Download:

Hercules Plugin

here's the catch
the reason why the *recalculatestat is still broken on rathena,
is because how the way script handling the event queue

Supposedly, we want the bonuses from the OnPCStatCalcEvent: to execute before running the status_calc_pc function
but what actually happen is the script queue up the OnPCStatCalcEvent: label, execute status_calc_pc first
thus all the bonuses from equipment/job bonuses etc has been applied, then OnPCStatCalcEvent: come in too late

since I figure it out, my little trick is delay the OnPCStatCalcEvent: to run AFTER the script execution finish, using a delay timer
I actually explained this to Secret, rathena developer, but she doesn't agree with my method, because this is not thread safe
but whatever ~ at least it working for me


OnPCStatCalcEvent can't run during npc dialog, and spam map-server error along with it


easy to reproduce during the npc dialog, keep change the equipment, you'll lose the str bonus and map-server spam error

conf/map/battle/items.conf
change this to false should stop it

showdigit

official documentation from script_commands.txt

there is much more than this

because the decremental counter for type 3 runs 2 ticks per seconds, it is not advise to use type 3
type 2 is much preferred, but it risk in going into negative value, so this is usually how I do it
use showdigit type 2 for the countdown, then sleep for the duration, type 3 with 0 value will remove it

and to use this practically for an event, let's say the event only last 5 minute

========================================================

Now for the weird part of this script command

No.1 it run together with pvp counter

if you enter a PVP map, and try to showdigit, it actually show both of them
 so NEVER use showdigit inside a PVP map


No.2 log out and log in , the counter continues

this is another weird bug about *showdigit, it only send a value to the client
the countdown is totally handle by the client

for example from the script above,
if you log out inside the guild_vs2 map, just change character, log in back, the counter still shows
OnPCLogoutEvent also fail to remove it


External Links:

How to put countdown
showdigit will continue to countdown if the player logout

Tuesday, January 29, 2019

Units move diagonally then move in straight line

I'm sure those who have play firewall mage understand this
Because it is moving in such a weird behavior,
a normal distance script command, or Pythagoras distance check isn't accurate

When input X +5, Y -10, phythagoras distance function expected 1650 mili-seconds,
but travel_time function expected 1800 mili-seconds
the result is 1872 mili-seconds

Thus travel_time function should be use instead,
simple, just  (2min)² + (max - min) where min is X or Y, which one is smaller, and the opposite for max


See also:

Ragnarok Online use number of Squares to count distance  
A simple walking npc script

Monday, January 28, 2019

Daily Quest - use gettimetick(2) or gettimestr ?

Most people when thinking about Daily Quest, they think it as a quest repeatable every 24-hours


but my experience playing other MMORPG games, it actually reset at 12am
yes, I can finish this quest by 11.59pm, and redo it again at 12.00am



I would prefer to use *gettimestr method, unless being explicitly ask redeem the prize every 24 hours
I prefer *gettimestr over gettime(GETTIME_DAYOFYEAR) because I can read the value clearly
gettimestr("%Y%m%d", 9) return 20190128 



External Link:

OnHour00 players value recover 
Daily Rewards
Delete on char_reg_num on a specific time

Linux compiler error that never shown on Visual Studio

let's just admit Visual Studio sux ... ok ? right now, if you want to host a Private Ragnarok Online server, its just better to choose...