Where to begin... Okay, let's start with
== IDs ==
Everything in Mabinogi has an ID. Every creature, prop, item, quest, ect. They serve an
essential purpose: without IDs, the sever would have no way to identify the various entities when tell the client of some action involving them. However, devCAT has twisted and abused IDs to levels I would not believe...
- There are
ranges of IDs. For example, character IDs start at 0x10000000000001 and go up. If you create a character outside of this range somehow, the client won't let you log in to it.
- ID ranges further dictate information about props. A prop is "hittable"
if and only if its ID is in a certain rage - and that range changes per map, and even in subsections of a map.
- Quest ID ranges determine which tab and background color a quest should have.
- AoE skills like Windmill, Fireball, Spear of Light, etc, do not send a creature's ID for their target. Rather, they send a really funky ID that contains the coordinates of the area clicked, divided by 20.
[spoiler=Known ID Ranges]
// Id range starts
public const ulong Cards = 0x0001000000000000;
public const ulong Characters = 0x0010000000000001;
public const ulong Pets = 0x0010010000000001;
public const ulong Partners = 0x0010030000000001;
public const ulong Npcs = 0x0010F00000000001;
public const ulong Guilds = 0x0300000000500000;
public const ulong Items = 0x0050000000000001;
public const ulong TmpItems = 0x0050F00000000001;
// 00XX<word:region><word:area><word:id>
public const ulong Props = 0x00A1000000000000;
public const ulong AreaEvents = 0x00B0000000000000;
public const ulong Parties = 0x0040000000000001;
// Quests is probably 0x0060000000000001, but we'll leave some space
// between quests and items (quest items), just in case.
public const ulong Quests = 0x006000F000000001;
public const ulong QuestsTmp = 0x0060F00000000001;
public const ulong QuestItemOffset = 0x0010000000000000;
[/spoiler]
== Stats ==
DevCAT gave the client no shortage of stats:
[spoiler=Open at your own risk]
public enum Stat : byte
{
Name,
Title,
EngTitle,
Type,
SkinColor,
EyeType,
EyeColor,
MouthType,
State,
StateEx,
StateEx2, // [180300, NA166 (18.09.2013)]
Height,
Weight,
Upper,
Lower,
RegionId,
PositionX,
PositionY,
Direction,
BattleState,
WeaponSet,
Extra1,
Extra2,
Extra3,
CombatPower,
MotionType,
Life,
LifeInjured,
LifeMax,
LifeMaxMod,
Mana,
ManaMax,
ManaMaxMod,
Stamina,
StaminaMax,
StaminaMaxMod,
Food,
FoodMinRatio,
Level,
LevelTotal,
LevelMax,
RebirthCount,
LifeTimeSkill,
Experience,
Age,
Str,
StrMod,
Dex,
DexMod,
Int,
IntMod,
Will,
WillMod,
Luck,
LuckMod,
LifeMaxByFood,
ManaMaxByFood,
StaminaMaxByFood,
StrengthByFood,
DexterityByFood,
IntelligenceByFood,
WillByFood,
LuckByFood,
AbilityPoints,
AttackMinBase,
AttackMinMod,
AttackMaxBase,
AttackMaxMod,
WattackMinBase,
WattackMinMod,
WattackMaxBase,
WattackMaxMod,
LeftAttackMinMod,
LeftAttackMaxMod,
RightAttackMinMod,
RightAttackMaxMod,
LeftWattackMinMod,
LeftWattackMaxMod,
RightWattackMinMod,
RightWattackMaxMod,
LeftCriticalMod,
RightCriticalMod,
LeftRateMod,
RightRateMod,
MagicDefenseMod,
MagicProtectMod, // [180300, NA166 (18.09.2013)]
MagicAttackMod,
MeleeAttackRateMod,
RangeAttackRateMod,
CriticalBase,
CriticalMod,
ProtectBase,
ProtectMod,
DefenseBase,
DefenseMod,
RateBase,
RateMod,
Rank1,
Rank2,
ArmorPierceMod, // [180300, NA166 (18.09.2013)]
Score,
AttackMinBaseMod,
AttackMaxBaseMod,
WattackMinBaseMod,
WattackMaxBaseMod,
CriticalBaseMod,
ProtectBaseMod,
DefenseBaseMod,
RateBaseMod,
MeleeAttackMinBaseMod,
MeleeAttackMaxBaseMod,
MeleeWattackMinBaseMod,
MeleeWattackMaxBaseMod,
RangeAttackMinBaseMod,
RangeAttackMaxBaseMod,
RangeWattackMinBaseMod,
RangeWattackMaxBaseMod,
DualgunAttackMinBaseMod, // [180300, NA166 (18.09.2013)]
DualgunAttackMaxBaseMod, // [180300, NA166 (18.09.2013)]
DualgunWAttackMinBaseMod, // [180300, NA166 (18.09.2013)]
DualgunWAttackMaxBaseMod, // [180300, NA166 (18.09.2013)]
PoisonBase,
PoisonMod,
PoisonImmuneBase,
PoisonImmuneMod,
PoisonDamageRatio1,
PoisonDamageRatio2,
EnchantCombatPowerMod,
CumulateStr,
CumulateDex,
CumulateInt,
CumulateWill,
CumulateLuck,
CumulateHeight,
CumulateFatness,
CumulateUpper,
CumulateLower,
CumulateLife,
CumulateMana,
CumulateStamina,
Toxic,
ToxicDrunkenTime,
ToxicStr,
ToxicInt,
ToxicDex,
ToxicWill,
ToxicLuck,
LastTown,
LastDungeon,
ExploLevel,
ExploMaxKeyLevel,
ExploCumLevel,
ExploExp,
DiscoverCount,
ConditionStr,
ConditionInt,
ConditionDex,
ConditionWill,
ConditionLuck,
ElementPhysical,
ElementLightning,
ElementFire,
ElementIce,
BareAttackMin,
BareAttackMax,
BareWattackMin,
BareWattackMax,
BareCritical,
BareRate,
}
[/spoiler]
- Many if not most of these stats seem unused or redundant, which begs the question: why do they exist?
- The server sends "StatUpdate" packets to the client whenever something changes: loss of HP, increase in stamina rengeneration, etc.
THIS PACKET IS SUPPOSED TO CONTAIN ONLY THE STAT(S) THAT WERE AFFECTED, THUS SAVING TIME AND BANDWIDTH. However, for reasons we can only guess at, each StatUpdate packet contains nearly every stat - updated or not.
- This packet is sent
many times when it isn't needed. It's as if the server is littered with code "just in case". This has an impact on the overall speed of the server and the client.
- The introduction of stats from Skills and Talents (ie, 128 bonus hp) is totally not accounted for. I can best describe it like this: When you log in, the bonus is reapplied. This has led to an interesting phenomenon of having
more than 100% of a stat, as evidenced by the character on the left's HP (shown in white over her head)
[Image: http://img818.imageshack.us/img818/2277/7dgy.png]
It also means that you can full heal yourself just by relogging several times, as seen here:
http://youtu.be/94lg2q7jHTs
For details on the (simple) solution, you're welcome to read this thread:
http://dev.mabinoger.com/forum/index.php/topic/537-171-life-to-lifedelta/
== NPCs ==
- NPC dialog is handled in a terribly awkward way. First, the text is XML-encoded (replacing < to < for example). Then the content is inserted into an XML template. This template contains details of a function prototype in the client to call, as well as arguments to pass it. What?!
Finally the template is bundled into a packet and sent to the client. This is how Duncan's "intro" appears when sent to the client:
[spoiler=Example]
[CODE]<call convention="thiscall" syncmode="non-sync">
<this type="character">45035996285xxxxx</this>
<function>
<prototype>void character::ShowTalkMessage(character, string)</prototype>
<arguments>
<argument type="character">4767482418037552</argument>
<argument type="string"><npcportrait name='NONE'/><title name='NONE'/>An elderly man gazes softly at the world around him with a calm air of confidence. <br/>Although his face appears weather-beaten, and his hair and beard are gray, his large beaming eyes make him look youthful somehow. <br/>As he speaks, his low voice resonates with a kind of gentle authority.<br/><button title='Continue' keyword='@continue' /></argument>
</arguments>
</function>
</call>[/CODE][/spoiler]
- Buttons are little more than temporary keywords
- As time went on, devCAT has gotten lazier and lazier. Button keywords (the text returned to the server, indicating what you clicked) went from "continue", "agree", "shop" to, I kid you not: "asdf" and "xxxx"