FFXIV 1.0 Classic Server Dev Blog

Actor Parameter JACKPOT!

I unlocked a huge cache of information yesterday after finding a certain hashing routine in the game's code. One of the actor modification packets; 0x179 edits the huge amount of properties that can be set. These range from your basic HP, MP, TP, to stats, to things like what hotbar button 3 is. They seem to be sectioned into different classes or objects, and then hashed into a single integer to be referenced. SU knew some of these ID's from their packets but didn't go farther than what was seen in the packets.

I first began with a memory search using Cheat Engine for "stateAtQuicklyForAll". This is a signal or function that must run to update your main three stats. I assume it's named this way because it's properties that change quickly (IE: using spells quickly changes MP, healing/taking damage quickly changes HP). Anyway, I found a huge chunk of what looked like JSON. Incomplete but it showed me what the "true" names of these properties were.

{
{"stateAtQuicklyForAll",0.3,
{"parameterSave","hp",1,},
{"parameterSave","hpMax",1,},
{"parameterSave","mp",},
{"parameterSave","mpMax",},
{"parameterTemp","tp",},
},
{"stateForAll",1.5,
{"parameterSave","state_mainSkill",},
{"parameterSave","state_mainSkillLevel",},
{"parameterTemp","targetInformation",},
},
{"potencial",1,
{"battleSave","potencial",},
},
{"exp",
{"battleSave","skillLevel",},
{"battleSave","skillLevelCap",},
},
{"bazaar",1,
{"eventSave","bazaar",},
{"eventTemp","bazaarRetail",},
{"eventTemp","bazaarRepair",},
{"eventSave","bazaarTax",},
{"eventSave","repairType",},
{"eventTemp","bazaarMateria",},
},
{"linkshellIcon",1,
{"eventTemp","linkshellIcon",},
},
{"stateAtQuicklyForSelf",1,actor,
{"parameterSave","state_boostPointForSkill",},
},
{"commandDetailForSelf",1,actor,
{"parameterSave","commandSlot_compatibility",},
{"parameterSave","commandSlot_recastTime",},
{"parameterTemp","maxCommandRecastTime",},
{"parameterTemp","forceControl_float_forClientSelf",},
{"parameterTemp","forceControl_int16_forClientSelf",},
},
{"commandEquip",1,actor,
{"parameterSave","giftCommandSlot_commandId",},
{"parameterTemp","otherClassAbilityCount",},
{"parameterTemp","giftCount",},
},
{"battleStateForSelf",1,actor,
{"battleTemp","castGauge_speed",},
{"battleSave","skillPoint",},
{"battleSave","physicalExp",},
{"battleSave","negotiationFlag",},
},
{"timingCommand",1,actor,
{"battleTemp","timingCommandFlag",},
},
{"battleParameter",1,actor,
{"battleTemp","generalParameter",4,},
{"battleTemp","generalParameter",5,},
{"battleTemp","generalParameter",6,},
{"battleTemp","generalParameter",7,},
{"battleTemp","generalParameter",8,},
{"battleTemp","generalParameter",9,},
{"battleTemp","generalParameter",10,},
{"battleTemp","generalParameter",11,},
{"battleTemp","generalParameter",12,},
{"battleTemp","generalParameter",14,},
{"battleTemp","generalParameter",13,},
{"battleTemp","generalParameter",15,},
{"battleTemp","generalParameter",16,},
{"battleTemp","generalParameter",17,},
{"battleTemp","generalParameter",18,},
{"battleTemp","generalParameter",19,},
{"battleTemp","generalParameter",

This chunk wasn't complete because the game overwrites a lot of the memory by the time you see it. However it gave me a lot to work with. First I isolated the packet that caused this to appear in memory. It seems the processing was done in a lua script which called C functions, so I put a breakpoint on lua_pcall and waited till I saw the above appear in memory. After that all I had to do was isolate where a known ID (hp for example) appeared in memory, and then I was able to find the specific function!

I didn't take a look at the algorithm itself yet and decided to first write down all IDs. I have figured out 95% of the IDs that get initialized at login, and looking through other packets I've been able to understand ones I didn't know about before. So with all this knew information I can now:

  • Set all general stats (STR, INT, Fire Resistance, Attack Damage, etc)
  • Set timers for status effects. Once I find how the log messages work, this will be done.
  • Add/Remove journal entries, as well as set guildleves as abandoned and completed
  • Set all action bar slots (all 30)
  • Set Bonus Point stuff
  • Set next combo, ws/spell timers, etc for action bar stuff
  • And a lot more that I haven't played with!

I AM ASSUMING DIRECT CONTROL

Over the last couple of weeks I've been slowly documenting the Lua engine inside FFXIV. From what I've seen, 1.0 uses Lua as a major core of their engine... handling things from UI control, to scripted events like interacting with actors. Thanks to the fact Lua is a open source project, I could compare the C++ code to the assembly of XIV and find the various functions. I have about 70% of the lua API documented and figured out. So now I can look into a routine and see something like this:

http://i.imgur.com/fRRcJjO.png

A lot of CALL functions with their proper routine name instead of some address. After getting enough documented, I was able to write and DLL that let me hook into those routines and "ASSUME DIRECT CONTROL". I can upload code into XIV's instance of the Lua engine and see what is going inside, and theoretically fire off/modify functions as well!

I'll leave you with this image. After testing a few basic commands (print, print version, and some failed global prints), I dumped all of the global table (_G). You can see a lot of classes FFXIV had uploaded into the Lua engine (IE: ActorBaseClass).

http://i.imgur.com/MyPm07O.png

First Look into Multiplayer

I've begun looking into setting up the multiplayer component of the server. I've been looking at the FFXI private server "DarkStar Project" to get an idea how to implement it since this is my first foray into MMO server design. It seems they are doing basically what I am when it comes to packet handling, so understanding how they managed the player instance helps.

The first issue I came across is the zones themselves; FFXI was split into many zones like FFXIV ARR was. On top of that, each zone transfer was actually a disconnect/connect (this could be to the same server) allowing for multiple servers hosting different zones. With FFXIV, the zones (yes, there are zones) seem to be all one thing, and as known; are gigantic. Also, from looking at the sniffed transfer between Black Shroud and Thanalan, there is no disconnect/connect. So one server is handling all zones. That doesn't seem right, but maybe I am missing something.

Now, DSP seems to give the FFXI client all actors/entities in the zone all the time. This isn't too bad due to the zone sizes they are dealing with, but would be killer with FFXIV. For example, all actors in Black Shroud would include all npcs/players in Gridania, and all npcs/players in North, East, South, and West Black Shroud! So I designed a simple filtering system.

I subdivide each zone into a grid based on a min/max value and a grid size. Each cell holds a list of actors in that one cell. As a player (or any actor) moves across the grid, I add/remove them from the cells they cross over. This way, I can just request all actors in their cell and the adjacent ones, adding/removing/updating the actors in their instance as things change. I don't know how well this would work in a 500 player situation but as a temporary measure to get things working, it seems to be sufficient! On top of that I added a bunch of helper methods in the actor class for generating all the necessary packets to spawn them (or change a specific attribute).

I didn't get a chance to test real time movement between actors because I didn't have a second installation of 1.0 to test with. However I didn't implement removing an actor from the zone on disconnect so I was able to switch characters and see the previous one where I logged off at! Since actor movement is very simple, I am pretty sure it will work when I get a chance to test it.

Here is an example of what I mean. "Test Test" was disconnected at that position, so when I logged in as "Localhost Character", I could see him standing there! Oh yeah, emotes implemented.

undefined

Huuuuuge Progress!!!

It's Thanksgiving here in Canada and I've stuffed myself full of turkey, however apart from the festivities, I've made a lot of progress the last few days. I've discovered countless packets; some through the sniffed packets, but others were discovered through guesswork and trial & error!

First, I worked on figuring out the 0x19x and 0x1Ax packets. These seem to be all the "fluff" info from quests completed (your Unending Journey), to your Chocobo name, to your title, to your achievements. The following information has been discovered:

0x0145  Set Actor Icon (Busy, GM, Disconnecting)
0x0194  Set Grand Company Info
0x0197	Set Chocobo Appearance
0x0198	Set Chocobo Name
0x019A	Set Achievements Completed
0x019B	Set Top Achievements
0x019C	Set Total Achievement Points
0x019D	Set Player Title
0x019E	Achievement Earned Packet	
0x01A3	Set Completed Quests (For Book)
0x01A4	Set Current Job
0x01A7	Set Dream Result (Required for wakeup)

Because I can set the quests completed, this also means the Unending Journey has access to all the cutscenes! It seems however some will not load, probably because a Path Companion has not been set. Gotta figure that one out.

Funny enough, 0x19E was just a guess! With that figured out, I tried my luck on other guess. I made a packet of 0x28 in size (most packets are this size), and just began going through opcodes from 0x02 and up. I found the following:

0x0004	???Begin Zoneout?
0x0007	Delete All Actors
0x000E	Logout
0x0011	Quit Game	
0x00CD	Unset Actor Class/Script?
0x00D3	Set Actor Target (Animated)
0x00D4	Turn Actor to Target
0x00D7	Flashes Appearance, Sets Actor Target
0x00D8	Appearance Related?
0x00DB	Set Actor Target
0x00DC	Set Actor Target (Unknown)
0x00DE	Clears Actor Target (Seems to)
0x00DF	Set Actor Target 

As you can see, some are just guesses at what they do, however finding out the animated "turn to target" was great cause it let me do the head turning thing characters in ARR do! Also Logout and Exit game, YES! Back when I was modding SU I got the menus for the Bed object and Quit option to pop up but didn't know how to actually log or quit. Now that can be fully implemented.

A lot of this info was found while examining a player object in the game. With this information, I probably have everything needed to create player actors in a player's instance. I think I have the groundwork for multiplayer! However I won't start that until I finish more of the login packets and get the player out of the inn and into the world. I'll leave you with this video of me testing player spawning and moving:

Small Things

I decided to clean up the hardcoded packet a bit more and remove subpackets yesterday. I already knew how some of them worked but never got around to implementing them on the new server. Mainly the "SetActorPosition" packet which is used to place an actor in the world. Interestingly I found a value that changes how the actor appears; when set to 2 or higher it does various effects like the one that happens when a person teleports to you in ARR. Watching old 1.0 movies it seems this is also played when you appear in a Duty.

The 0xD0 packet was finally figured out as well. I knew this had something to do with speed, but at the time I couldn't figure it out. Turns out I was right, it sets the "slid" (always 0x00), walk (0xA000) and run (0xA010) speeds. Simple.

Finally, I found where the "Resting" status effect is set but I am not even remotely close to figuring it out. It seems it uses the same begin/end tag system items used. Still, was able to add a few codes and get other statuses to appear.

undefined

Equipment and Actor Name/Appearance

Yesterday I knocked off the last of the item packets. Specifically this was the "initial equipment setup" packets; 0x14E. I call them them this because a separate packet is used for equipping/unequipping an item. Simply, it's a list of short/int pairs that specify what equipment slot holds what item, using the inventory slot number. If there are more than 8 items specified, the remainder is put in another packet. If a slot isn't specified, the game assumes unequipped. Simple really.

This also was the last of the items packets in my 5 hardcoded "login" packets, so woo progress! The first picture was my initial test. Funny enough the game doesn't care what the item is itself, and will gladly put Chocobo Masks all over your body. The second picture is the finished product.

Also as you may have noticed, I've implemented the actor name and appearance packets. These are use to set any actor's... well, name and appearance. An actor can be anything intractable, from players, to monsters, to even the bed and cutscene book in the inn room (notice the green dots in the minimap). So no more Mr. "Name Goes Here". This was already known and setup, I just never bound it to a DB. Now that it is, the lobby and game appearance will match, and I can even setup appearance changes when you equip a different set of armour! However before I can do that I will need to make a master list of Item -> Item Model because these two pieces of info aren't stored together.

And just a note: While I have items equipped, you may ask "well why aren't they on your character?". This is because your appearance and what you wear are separate things. Your appearance (well, your actor's) is just a set of model IDs that the server updates when equipment changes. I haven't implemented that yet. This is also how glamour in ARR probably works. They send a different model ID for your appearance packet. Funny enough, the first value is a base model ID used to set monster models or the base race model, so theoretically you could make the player into Ifrit.

undefined

undefined

Newer posts → Home ← Older posts