27. Juli 2010

I took some time to find unknown opcodes the client can possibly sent.
I found a few, for example an opcode sent by the client when he receives a new spawn (player) with an unknown guild id. Normally all guilds and their ids are sent upon zoning, but if a new guild is created meanwhile the client does not know anything about the guild (name etc).

I also found a client command i had forgotten about or never used anyway, the /channel command. Apart from the server side filter command this command enables you to turn off the specific chat channels like gsay,ooc,auction,shout,ooc etc. The client can inform the server that he wants to either turn on/off a channel. I identified the requests from the client, we only need to add the filtering to the server which shouldn't be hard.

~ 240min
= 10020min (167h)

26. Juli 2010

I figured out the checksum stuff, the client sents checksum for a few game specific files, and also each time for some specific actions. That the checksum is over 2000 byte was very confusing, but in reality this is all what they wanted, confuse =). For the specific game client files i added a checksum method server side, if the checksum do not match the player will simply be disconnected.

Basically, this means we have a base line of defence now for easy file hacking.

~ 600min
= 9780min (163h)

20. Juli 2010

I found some interesting opcodes previously unknown, they are the checksum of some specific files (i.e. spdat.eff) that gets transferred to the server. This way it is possible to easily detect modified files, i still have to reverse how the checksum is calculated (its quite huge! over 2000 byte) currently i could only hardcode a "valid" checksum because i know the client has a valid file.

Additionally, i also extended the trigger and traps framework to be able to spawn a random amount of npcs at a specific location (i.e. beetles in necropolis for example).

Finally i've spent alot of time of preparing a video, which is not easy todo because you can die pretty fast from a train triggered from these traps =)

~ 480min
= 9180min (153h)

13. Juli 2010

Since this weekend i was working on finishing the traps and triggers. I gathered over 50 different trigger messages and traps from live, funny that the zone with the most zone specific "roleplay like messages" is the vanilla zone runnyeye. Other messages happend mostly in velious zones, but also very rarely. I added a database based configuration for these triggers. Basically you just can have an existing invisible npc (or create on in the database) and define at what proximity a trigger should happend. A trigger can either be just a message, an AE spell, or a group of spawns at a specific location or all of them together.

The first spawn based trap i added was the one for frozen tower, when you enter the zone just to the left 3 shadowbones will spawn. There are more zones with triggers like this, necropolis and runnyeye comes to my mind, im not aware of any other currently.

I also worked on the spell effects a trap executes, i had issues getting the spell effects visible to the client, after a bit of debugging i found out that the invisible npcs where not sent to the client, and when you want to see a spell effect you supply a caster ID of the mob who casts the spell. If the client doesnt know this id, you will obviously see no effect.

I hope i can make a video this week about triggers, since i will be gone for a short vacation for the rest of the week.

~ 1200min
= 8700min (145h)

9. Juli 2010

The last two days:

I fixed some doors in necropolis and pendulum traps, and added the missing qeynos doors to a delta sql. Then i create a db table for triggers with different attributes and integrated it into the base code. Triggers can now be easily assigned to "hidden" npcs. They can either cast spells, summon X mobs or just print a message. Then i made certain that mobs or players cannot attack triggers.

I also added the 15 different traps for necropolis including their spells and trigger messages. I started with exploring EQ Live again and started gathering all the environment messages i have the location of: chardok, frozenshadows, kael, runneye, westwaste. I checked some other zones i had some info on but they did not have (anymore?) the messages. There are a few kunark/velious zones left i have to check.

~ 780min
= 7500min (125h)

7. Juli 2010

I added environment message and spell support to the triggers. Additionally had to do some modifications to the spell system since it didnt allow clients to be target of AE spells. Furthermore traps should not get on the hatelist of mobs when they are triggered, only the client who activated them should be on the hatelist.

~ 540min
= 6720min (112h)

6. Juli 2010

I worked the last two days on a TriggerEntity framework. Basically it is currently suited for Traps or Messages which will be triggered when a player gets near them. These Entities are invisible mobs but they are actually in the zone. I also added some dev client commands to show these triggers to the client. Currently the only thing which happends is that a message is sent to the players (You triggered...). What also works is that a client can now sense these kind of triggers if they are traps (along with the normal SolA+B traps).
In the next days i will add spell/message support to these and finally need to put my collected data into a database table, i.e. trap ID x, do spell Y when client is near proximity Z and say a specific message. Additionally i need to add a disarm check to these so they dont trigger when a rogue/bard disarmed a trap temporary.

~ 480min
= 6180min (103h)

4. Juli 2010

That was some major research on eqlive the last two days, i got now 13 of the 15 different traps in necropolis, their spells and trigger text. At first i was just doing sense trap every few meters but after a while a thought this is too tedious, therefor i had the idea to just put the traps on the ingame map. Since i already have most of the possible trap locations i only needed to convert this information to the eqlive map format. This is what i got out of it:

However even with this info i triggered a trap more often then not, sense traps is very hard on live now. I started with 1 skill on this rogue and its now at 60, even when i sense traps 3 times before each spot - there is a high chance i miss the trap. These are the reasons for death mostly, also since i need to trigger the trap anyway to know what it does it sometimes means also death. Disarm on some of these traps is a pain, at first i thought its a range issue, but it wasnt, i literally took up 20 tries to disarm some specific traps here even with a skill of 60 disarm traps. Talk about wasted times since these skills are on a cooldown of about 10sec.

Since the chance to find the last 2 traps is less then 10% i will stop here and use a similar effect i see fit, maybe somebody else i bored and will do this, but i have enough of necropolis =).

After that i went velks lab, thankfully there are only 2 different traps here which i found easily using above method.

Finally i went to chardok and found the different traps on the bridges and the text/effects.

There are one or two traps in the frozen tower which doesnt seem to do any damage, which will also be added.

So, this is it - as you can see about 24h went just into researching this stuff without any line of coding done =/.

~ 720min
= 5700min (95h)

2. Juli 2010

Went into necropolis on live, thanks to Neorab who borrowed me his rogue. Identified about half of the 15 different traps, the text they generate, the effect (spell/mob), if they aggro or not... Pretty time consuming since i've died a few times (the rogue is only lvl67...wait now he is lvl66 =/) and the run from PoK to necropolis is about 15min each time.

~ 360min
= 4980min (83h)

1. Juli 2010

I did some research on the entities based traps (invisible mobs that trigger when you are near them). I think i identified most of them now and even have the positions where they are, what i dont have is what each trap does actually do. I need to do some more research on this but some should be pretty clear by now. I should have enough data now to proceed with the development of these kind of traps it will be generic enough that missing traps are just a database entry.

~ 240min
= 4620min (77h)

28. Juni 2010

Alright, after a few more hours of reverse engineering i gave up for now on trying to trigger the traps client side. I tried everything i could think of, like i thought that a player had a hitbox associated in the zoneentry struct - i even found a new value, the players size (strangely, shrink/grow could theoretically persist between zones fine) but it do anything. I also found another strange value, basically it will automatically pan your first person camera up or down very slowly automatically when you login. Depending on the value this can be done faster, but the issue is after a certain degree is reached your mouse will have the left and right side mixed, you are basically staring at the roof and in first person it looks like you are walking on the roof.

I also found that the client had a check when you got near a trap, it removes any invisibility spell - the funny thing is, this only triggers when im standing at very specific locations at a trap. This initially let me believe that there must be some kind of radius i need to set how big the area should be the trap effects, but it was still hopeless =/.

Anyway, i finished the sense trap skill and the disarm trap skill. There are two possibilities to use the disarm trap skill - either click like mad on the trap itself, or use the disarm trap skill button, on the server the nearest trap will be located and then it will be tried to be disarmed. Once a trap is disarmed, it will stop for about a minute and you will not "sense" it anymore since it is disarmed, you will sense another trap if any is nearby, same applies for disarm.

Currently whats missing is obviously that no damage is generated from traps, i had to do that server side anyway for the kunark/velious traps which were just invisible mobs which triggered on proximity instead of "on hit" like the few dozens classic traps in SolA+B and paw.

Instead of 99%, i am now 99.99% certain that there is no damage trigger client side and it was all generated server side back in the days.

~ 840min
= 4380min (73h)

25. Juni 2010

I started to work on Traps and their associated skills sense traps & disarm traps. I figured out what kind of packet the server has to sent to the client, that he will turn into the direction of the next trap.

I also figured out the disarm trap skill so far, it seems there are two possibilities here - a rogue with at least 1 in disarm trap skill can click on a trap OR just use the skill disarm traps to execute the disarm action. In the first case it is similar handled to opening a door (client sents to the server a request with the door/trap ID), in the latter case its just the info that the client wants to use the disarm trap skill without any associated trap. The clicking on the trap is rather easy at first, but you had to figure out what to sent back that the trap visually stops moving. Its another value in the open/close door action which we already use for closing/opening doors on the client.

So visually disarm trap for clicking a trap works, i need to do some server side code for using the skill button to figure out which is the nearest trap next to the player, same goes for sense traps. This is the easy part.

However, i spent most of my time debugging the client and figure out why no damage is generated from traps when you walk into them. The damage should be done client side i think, its the same with drowning or falling in lava - the client recognizes this event and will do damage locally and only sent the info (and the damage done) to the server. I went as far as logging into EQL and going to SolusekA to take a look how the traps work, the damage of each trap was hardcoded and always the same for each different type. What is funny is that using the damage value information i was able to find the client side function, which returns a different trap damage value depending on the trap type (pendulum, spears, saw). I spent most of my time reversing were this function is called from - but there was no indication that the client would do a check if the player is near a trap - damage him.

I will spent some more time reversing the client and trying to find out why the trap doesnt do damage, might be still a specific byte in the door/trap struct we sent to the client to spawn these door/traps - who knows. I currently think that the older client didnt trigger the damage, it was done server side and later changed to be client side - it would be easy to do this on the server (similar to sense traps, if a client gets near a trap the server would damage him). However it would have the visually effect of doing damage to the client, even when the pendulum is currently not hitting the client since the animation is done by the client the server cannot possibly know when it would hit the player, it can only know that the client is standing where a trap is.

When you think about it, that there are only 20 real traps in classic in 3 dungeons (soluseka,b and paw) - thats alot of engineering going into such a small part of the game =).

~ 660min
= 3540min (59h)

23. Juni 2010

Pretty boring bug hunting, trying to figure out why the zone crashes when it is statically loaded, i.e. all NPCs/Stuff already started before any client ever connects. Had something todo with the Perl Interpreter not being available to the EntityList, i have no idea why this happends as it only happends when the zone is statically loaded. When it is loaded dynamically (i.e. when the client wants to connect to a zone, the zone is loaded with NPCs and stuff) it works fine. Maybe because the instance of the interpreter was created in the main thread instead of sub threads? I fixed it but sadly im not sure why it didnt work before, this happens when you are more a generalist instead of a specialist in one specific programming language. This alone took over a day =/

I additionally added the last missing things for range attack, rogues deadly strike, i guess range attack should be done for now. Also some refactoring on some ugly code like melee kick attack. Warriors at or above lvl55 now have a chance to stun their target when kicking.

~ 720min
= 2880min (48h)

21. Juni 2010

I just wanted to try something out, but only ended up creating some work for myself.
I wanted to check some things out in kedges keep and teleported there, of course i forgot to cast an enduring breath spell - but that wasnt the major issue. As soon as i zoned into underwater i started to drown, which should normally not happend because you got an air bar as soon as you dive into water.

Well i figured that how much air the player has left has not been identified in the playerprofile, so we probably always sent 0 when the player zones. I searched with the debugger in the client to identify the function, where the "air is left" check is done. At first i thought i was quick finding it, i set the value to 255(0xff) server side and tried to relogin while being underwater, but it didnt help - i started to drown again. Confused i tried some other unknown fields we had - same result. A few hours later i figured, on the client there was another function to sanitize the air left value while checking - 255 wasnt a good value. So i started with the first field in the playerprofile i thought i had found. I set it to 10, and zoned in - i didnt start to drown immediately but the air left window at least popped up and was almost zero. After a saw that the client had 2 max values for this field - it was either 100 or 127 for iksars (they can hold their breath pretty long =). So after this field was identified, i could save it when the client requests a save - and the next time the client zones it will take the last saved value. So, you should drown instantly as soon as you dive from qeynos to qcat =). The time on this field is approx in seconds, it seems the client generates this value out of the endurance/stamina the client has. So normally a client can dive over one minute, or iksar over 2 before the air runs out - pretty long i must say, didnt remember that. Fun fact: the client substracts 0.125 * YourHP every drowning interval, so you are pretty much dead after 8 intervals, no matter how much HP you have left.

Next i added the broadcasting of tradeskill combine/failures to the group, as it was classic as pointed out in the forum - the part was already there when i reworked tradeskills last year but i left it outcommented because i wasnt sure it was classic.

Finally, i added a new gm method #listentities to view all the npcs that are currently spawned in the zone along with XYZ. Along with this its easy to use /goto a_mob_00 to teleport to him.

~ 300min
= 2160min

18. Juni 2010

Well, after some more testing with the items i committed the changes, but only after fixing another issue. I was trying out the summon spells, since they summoned a wrong amount of charges. I got to try out gift of xev, it summons a bag and bandages and food and stuff. So i put the bag in the inventory, opened it, and wanted to put in the food,drink and bandages i got on my cursor.... well but the client said item to big..lolwut? Then i tested other bags, and even tinkerer bags were "tiny" - i.e. only tiny items could be put in.

I checked the loading code from the database and it seemed fine, i didnt understand why the bagSize was always 0.... well after a while it turns out it was a really stupid mistake. I was always fixing the race/class bitmask (see earlier posting) at the very end of the loading code for each item. Well, however these fields in the item struct are only used by "normal" items, not by books or bags. When you set the race data in the struct, you effectivly overwritting the bagSize property cause they are both at similar position in the itemstruct bytewise. So it went like this: load item from DB -> ok its a bag -> set bag attributes -> do stuff -> finally set race/class bits == same position in struct as bag attributes -> overwrite these values again (with 0 in this case = 0 = tiny bag size).

It must have been a design decision by verant very early to missuse the same struct position of an item depending on the item type - to save some network traffic i guess.

As far as summoning items is concerned i only found one spell which really have a level based code for the amount of charges returned -> summon bandages (level/2) - any other spell would always return the max value regardless of level (20 arrows/daggers/food/drink for Cornucopia and co).

~ 240min
= 1860min

17. Juni 2010

I figured out the issue, that for example bows were not showing a 3d graphic on the model. The newer item table has other "idfiles" associated with many items. An idfile tell the client for each item, which model it should display when it is equipped. Now the newer table had idfiles like IT10649 - a very high number - the classic client doesnt know about newer model files. In this case, this is a newer sword graphic, but in the blob table the idfile was still IT1.

I started to search for items, where the idfile attribute in the item differs. After about an hour and more then 15 different idfile mappings i came to the conclusion that proceeding a manual mapping for each item would be an insane job, which no real programmer would do =).

So i wrote a program which compares each item from the blob and non blob table, and takes the idfile information from the old table. When an item is not found in the blob table but in the newer one, i used the idfile of a similar item which had a similar new_idfile->old_idfile mapping. The program procued an SQL delta update file which sets the idfile for all items which existed in the blob table. This way we should have a relatively complete item table but with the correct idfile mappings of older clients.

I verified alot of things i could think of with the new item information

- clickies
- trades
- buying
- equip
- check item description

i havent found an issue, the client doesnt complain. There should be no issues for new characters, but maybe some items are missing for existing ones, therefor before i do a commit i let YL test it on his chars if everything works out.

I also toyed around with the loginserver, i did not try it out yet. I got it to compile quite easily thanks to neorabs description txt. For developing, we just skip the loginserver and connect directly to our local server, you are directly transfered to char selection instead of going to the server select.
Was funny to see the old interface, i clicked on Create Account and remember the screen where you needed to put in your CD key, name, address etc to get your account approved. There wasnt a webpage to register in classic, everything was done using the client.

~ 480min
= 1620min

16. Juni 2010

I started migrating the non blob item tables into the main code. Currently we use an old item table which contains like 2 rows, the item id and a binary blob of the data which represents the item. Last year i was successful in creating items (fill our item structs) from a non blob table, i.e. this table contains a row for every attribute of an item. This makes it easier to fix or add missing items.

The first issues i had where the number of items, newer item table dumps contain alot of items (which we will not use), and the eqclient only supports 16bit integer MAX number of items. The item # in the item struct is just 16bit, since i forgot about that i was seeing all sorts of weired things happening when i logged into the zone, corpses had 20 items on them which looked like gloves, my equipment vanished mostly, i was getting alot of "bogus item received, item deleted" messages from the client. The glove item was in the item table exactly at # 16bit int max, so it overwrote the array each time =).

The next thing with the bogus items was, the race and class bits on each item. EQ uses a bitmask to know which class/race can use an item, each class/race represents one bit like 0001, another class is 0010, now when you have 1111 it means all class/races can equip this item. So, the issue here was that the newer item table dumps included a higher bitmask for all classes/races - because in the years more classes and races have been added, so i just needed to remove the higher bits. That means the items use a bitmask of 11111111 11111111 - but the client only knows up to
01111111 11111111 for classes, and 00011111 11111111 for races. After this fix, you wouldnt get any messages about bogus items any more.

Now i tried a few things, like shopping, dropping, trading etc the newer item tables
seems to work fine so far, i only found one issue - some bows do not show a graphic when you equip them. The item struct contains like over 100 attributes, and only a few bits are still unknown to us, these seem to have a meaning and are causing this issue.

~ 420min
= 1140min
I refactored the range attack code this time, it looks much clearer now. I did quite some research on some hard numbers for the damage calculation. I found some info on alakazahm and the monkly business forum. The rangers glade forums have not been archived, only the front page so i did not find anything worthwhile there. The information i gathered is that range attack calculation is similar to melee combat, except that it uses DEX as a primary attribute and not STR. Therefor i used the miss calculations from the melee combat, if we do any changes there it will automatically apply to range combat.

I didnt know that there where range items with a proc, but since i was testing bow damage i found one of the top bows from a PoA quest - windstiker which has a cool proc effect Whirlwind. The mob spins around itself like M.J. dance. Turns out there wasnt any code for handling proc damage from a range item, which i therefor added.

As i was equipping my ranger with some nice tolans gear, i saw that they all had nice spell effects. The bracers had summon arrows, i tried it out and for the long casting time it had - only one arrow came out. That couldn't be right so i tried some mage spells which summon 20 charges of food - the same issue, only one item. Turns out that the max charge info was not honored in the spell code for summon items, i fixed that and the correct amount should be summoned now (the information is found i nthe spdat spell info and seems to be correct, i checked summon bandages which should summon 5 bandages and all other mage spells, seemed fine).

~ 300min
= 720min

8. Juni 2010

I tried (again) to figure out why the first spell gem ignores the recast timeout sent from the server to the client. The recast timeouts are an array of int with milliseconds till cooldown is done. It works flawless for spell gems 2-8, but not for the first. Its frustrating to see it should be so easy, yet it is not. Its similar to the issue i had with disciplines not working (executing /disc xxx never reached the server) because one bit in the profile was not set (one of 10000 =p).

Anyway, in my reverse engineering i figured that the client has 2 different checks for the recast delay, one is executed for the velious fullscreen ui, one for the classic UI (it can drive you insane when you just found a breakpoint, but for no reason it doesnt work the next time you start the eqclient). The check points to some memory location depending on the spell gem number, if the value is 4, the spell is on a cooldown. Well, now the issue is i havent found where this value is set - there must be a separate thread which countdown the recast delay and set this.

So finally, i came to the conclusion that it mostly aint an error in the playerprofile, why?

When you set the value in the profile, which is loaded once you zone in, the spell gems are greyed out the second you are logged in, except for the first one. There is a similar client side mechanism when you cast a spell - the client knows what the recast timer the spell has. So i did a small test finally, I cast a spell with a recast of 12sec, like bind affinity - on the first gem slot the spell was available immediately (after global cooldown), the same spell however had the correct recast cooldown in any other slot. So this leads me to believe that this is indeed a bug in the client.

There is a slim chance its something else, but it doesnt look like it - i could only be certain when i had the executeable from either a later patch or an earlier velious one (we currently use the 22/08/2001 patch exe).

Its actually not even worth investing so much time into this, because its purely cosmetically (you cant cast the first gem anyway when the server prohibits it, but its not greyed out). However, issues like these are like riddles which just scream out loud to be solved by reverse engineering to get a better understanding of the client.

~ 300min
= 420min

2. Juni 2010

Ever wondered why the endurance bar went up and down very fast in the showcase videos?

Well, it turns out that the clients check for food/drink excluded GMs (thanks IDA Pro). They are never thirsty, however the eqc server did not know that, so GMs were mostly always thirsty/hungry server side and we would sent 0 endurance update to the client. However, the client thought otherwise (he also temporary updates the endurance client side), thats why - when the client updates the endurance bar a small bit of endurance was shown, but every few seconds the server sent 0 endurance back to the client.

I also added a check to remove endurance while being in water during the regular endurance calculation. It actually was already there but not enabled, because when Taz implemented the logic 2008, there were no water check support in the eqc server, i added that in late 2009.

~ 120min