EQ: Classless.

  • Guest, it's time once again for the hotly contested and exciting FoH Asshat Tournament!



    Go here and fill out your bracket!
    Who's been the biggest Asshat in the last year? Once again, only you can decide!

Secrets

ResetEra Staff Member
1,860
1,856
When does 3.0 launch, and is PvP enabled?

Not sure on launch date. There's a lot to do still, and a lot of systems we want to make, to make sure all of what we're doing will last into the future. As such, I don't have an ETA, but I know it's going to be 1-2 months at least of work to do casually.

PvP will be using the system from 2.0/2.5 where you can enable it optionally to bypass scaling. This will allow you to kill quest NPCs that are trivial fairly easily, as well as bypassing scaling down to NPCs but you also risk being killed by the people around you. As scaling is disabled they will also drop no augs, but as there are no augs in the item, they will also be potentially able to be traded. That won't do much if there's no augs, but at least the option to do so will be there.
 
  • 1Like
Reactions: 1 user

Secrets

ResetEra Staff Member
1,860
1,856
I have been trucking along with the classless 3.0 - there's been 2.0, and 2.5, but 3.0 is going to be pretty special. I'd like to share sort of a technical dive into some of the level of detail we're putting into the EQ client modifications that help us customize and display important information.

EQ historically relied on the client to do calculations regarding stats and displayed values.
I've taken that a step further and just moved all calculations that the client sees to be dependent on what the server tells the client it has. A lot of more modern MMOs (including EQ2) are doing this now
EQ's UI in luclin has this enum called EQType - UI developers use it to display information internally on the client to their custom UIs, and the default UI does it, too. I've gone ahead and replaced important stat labels/gauges via dll injection
This is amazing, because historically there was no way to do this without pushing a new client build (if you're SOE/Daybreak/Darkpaw) or by tricking the client into displaying the right values with a mathematical formula that makes them look right
So in example
EQ eventually had this system called tributes ingame, which you could sacrifice items you didn't need to a tribute master and get benefits for yourself. Internally, this worked a lot different than the end user would expect. You could send the client an item in a specific slot, and it'd try and consider it one of the many 'equipment' slots. For example, EQItems // Item Details for Benefit: Extended Enhancement 10 - it would apply the focus effect invisibly to the client.
This is EQEmu's worst-kept secret, as no one really understands how the system works there. Also, item packets are horribly big even when compressed through EQ's netcode, and items eventually were cached if you possessed at least a minimum of 1. So we could send the same item to the same slot twice and it would update the client with those stats. Or we could delete it and the client would cease to have them again. Either way, this is highly inefficient as we have no real control.

So that's what we did on classless 2.0/2.5 to get the items to work as tribute items. We made the server create a blank item and send the stats of said item out on the wire in the tribute slot. Every time the player changed their gear, we'd send this item back to them.
That wasn't good enough for me. So I researched more...

The UI is now fed stats that are cached from the server. The EQ client does not natively have this functionality, so I had to hijack its existing label functions via a hook. For those uninitiated, hooks allow you to attach to C++ functions at a specific execution point. They 'jump' to the section of code the hook allocates, do a bit of custom logic, and then return to the previous calling function with a new result. They can also use the original function they replace, so it becomes possible (provided you use the same calling convention and parameter types) to add or modify functionality, or even outright replace functionality in-line
Programs like MacroQuest2 are useful for this. However, we don't entirely want to promote the usage of cheat programs and their plugins as a person running a server 'for play'. Also, people are generally uncomfortable with running code they don't know on their machine in the form of executable files. So I came up with a solution to inject the DLL transparently to the end user, while running a modified, but highly stripped down version of MQ2 that allows me to do hooks in the ways I specify.
Traditionally, there would be no way to do this. However, Windows applications have a list of 'exports' and 'imports' associated with the Portable Executable (32-bit or 64-bit assembly) in .exe, dll formats. Exports are listed in any sort of executable. They're essentially a way to tell other applications 'I have this function you need'. Imports are the opposite; they tell programs that you need that function and will refuse to run until you have that function. They can be specified by name or ordinal, and can be used to get any sort of return value. So, of course, EQ has a bunch of imports and exports. Some of them are system libraries, some are provided by third party vendors to support the game.

In 2012, I decided to take my hand at modifying the Titanium game client for Project 1999. The technique was used to implement a fix to disable maps, the advanced tradeskill window, and enable a texture for skeletons that was previously disabled by SOE - I assume to implement the Luclin skeletons, as they were forced to 'texture 0' and the model for classic skeletons was outright replaced by a load order dependency in one of the global character files, so there was good cause to force them to load a specific texture set at some point in development.

However, Project 1999 was not allowed to distribute the game client. This is due to previous Cease and Desist letters from Sony Online Entertainment sent to EQEmulator stating they cannot share the game client or any of its executables or assets. That did not discourage me, and having learned about a DLL injection method from a friend in a for-profit private server community (who ironically works at Riot Games on Vanguard, their anticheat, now) I was able to replace a system DLL named wsock32.dll, which is traditionally used for networking and socket purposes. The code was deployed and it worked - It's virtually transparent to the end user if you include it in an archive and don't know what it does, additionally it has the benefit of allowing code injection without breaking the law by redistributing the program. But antiviruses became wary to the technique, as we were exploiting a technique that malware often uses - replace a system file in a directory and your program runs fine, but also loads the code in the directory and the program ignores the system file entirely. So we had a dilemma once players were finding it detected by AVs, despite being non malicious in code.

Players who knew nothing better thought it was a virus, and short of being tech savvy you can't really convince them otherwise that the code was not a virus and play. So we had to look at alternative avenues - enter the DirectX example code library hijack, where we replaced a file outright that was already in the EQ Titanium folder, DSETUP.dll, which was used to determine if the player had installed DirectX 9 or not. By 2012, virtually every machine was on Vista, 7, or higher and very few were on XP. So this was a golden opportunity to just say 'hey, you have DirectX9' in the function it exports - and also, when the DLL reaches its main entry point even before that (DLLs are loaded and their main entry point is technically a function that tells the program whether it's safe to load or not based on any exceptions or return values) where the program is loaded, but has not reached its own entry point. This allowed us to completely replace the one export in DSETUP.dll with a function that did nothing and allowed the program to continue, while injecting our own code.
However, DSETUP.dll was removed from the import list by 2007, well before I started this hijacking and reversing project, and future projects I did would require the use of newer EQ clients. They were often more reliable and had better options for UI, had the dual core optimization bug fix built in, the west bug fixed by default, etc... they were better clients internally. They ran faster, used less memory, and were generally 'better'. So I did the same technique, but this time with the DirectInput8 library, also from the DirectX SDK. dinput8.dll would be able to be plopped into the folder alongside the install, and would provide the same method of code injection.

I took a copy, as I had mentioned, of MacroQuest for the client in question and embedded it solely contained within the dinput8.dll file. I had to remove the plugin system from MacroQuest as it was no longer needed or relevant to the players in question.

We take a list of stats, send them to the client, and return the value from the server in the label function instead of the return value from the original label function, if the label ID matches the override. It works well albeit from a few edge cases.
// Construct packet opcode = OP_EdgeStats; outapp = new EQApplicationPacket(OP_EdgeStats, 4 + (sizeof(EdgeStatEntry_Struct) * ((int)(eStatEntry::eStatMax) - 1:emoji_nose:; itempacket = (EdgeStat_Struct*)outapp->pBuffer; itempacket->count = (int)(eStatMax) - 1; for(int guava = 0; guava < eStatEntry::eStatMax - 1; guava++) { itempacket->entries[guava].statKey = (eStatEntry)guava; itempacket->entries[guava].statValue = GetStatValueEdgeType((eStatEntry)guava); } QueuePacket(outapp); safe_delete(outapp);
That's how we construct the packet. It loops through all possible values and sends them. That is sent on changing any of the players' stat totals. There's also a smaller one for HP, Mana, and Endurance used.
EmuOpcode opcode = OP_Unknown; EQApplicationPacket* outapp = nullptr; EdgeStat_Struct* itempacket = nullptr; // Construct packet opcode = OP_EdgeStats; outapp = new EQApplicationPacket(OP_EdgeStats, 4 + (sizeof(EdgeStatEntry_Struct) * 2)); itempacket = (EdgeStat_Struct*)outapp->pBuffer; itempacket->count = 2; itempacket->entries[0].statKey = eStatCurHP; itempacket->entries[0].statValue = GetStatValueEdgeType(eStatCurHP); itempacket->entries[1].statKey = eStatMaxHP; itempacket->entries[1].statValue = GetStatValueEdgeType(eStatMaxHP); QueuePacket(outapp); safe_delete(outapp);
Those are sent when the player receives an HP update through another source.
So the game looks like it's working properly to the end user, but fundamentally works a lot different internally.
It's really impressive. And that's only the tip of the iceberg with this system. We've done a lot more and it looks great to the end user. It's a pain to do without the source code, but we do attempt to manage cool stuff here.

Classless 3.0 still has no release date. It's intended to be a complete overhaul, and to have systems I intended to have in from day one on Classless 2.0/2.5 but instead on release. We aren't going to half-ass 3.0, and I've been basically heads-down in code when I work on it and as such communication is minimal and limited to Ailia and Talco, as I am doing a lot of heavy lifting in the client / server. As well as doing my RL job too.

Additionally, I'd like to bring attention to Yawgmoth's new server, Monster Hunter Norrath, which should fill your 'classless'-like itch until 3.0's release. I plan on checking it out, and you should too. It's in no way affiliated with our team... I am more interested in learning from others and encouraging them to try new ideas. There are certain members of our community that are averse to having anything to do with Yawgmoth, and that's fine if you are avoiding it. However, it is a short term option for some players until 3.0 releases and it is there, so I figured I would mention it.

 
  • 6Like
Reactions: 5 users

Alasliasolonik

Toilet of the Mod Elect
<Banned>
4,908
9,890
I have been trucking along with the classless 3.0 - there's been 2.0, and 2.5, but 3.0 is going to be pretty special. I'd like to share sort of a technical dive into some of the level of detail we're putting into the EQ client modifications that help us customize and display important information.

EQ historically relied on the client to do calculations regarding stats and displayed values.
I've taken that a step further and just moved all calculations that the client sees to be dependent on what the server tells the client it has. A lot of more modern MMOs (including EQ2) are doing this now
EQ's UI in luclin has this enum called EQType - UI developers use it to display information internally on the client to their custom UIs, and the default UI does it, too. I've gone ahead and replaced important stat labels/gauges via dll injection
This is amazing, because historically there was no way to do this without pushing a new client build (if you're SOE/Daybreak/Darkpaw) or by tricking the client into displaying the right values with a mathematical formula that makes them look right
So in example
EQ eventually had this system called tributes ingame, which you could sacrifice items you didn't need to a tribute master and get benefits for yourself. Internally, this worked a lot different than the end user would expect. You could send the client an item in a specific slot, and it'd try and consider it one of the many 'equipment' slots. For example, EQItems // Item Details for Benefit: Extended Enhancement 10 - it would apply the focus effect invisibly to the client.
This is EQEmu's worst-kept secret, as no one really understands how the system works there. Also, item packets are horribly big even when compressed through EQ's netcode, and items eventually were cached if you possessed at least a minimum of 1. So we could send the same item to the same slot twice and it would update the client with those stats. Or we could delete it and the client would cease to have them again. Either way, this is highly inefficient as we have no real control.

So that's what we did on classless 2.0/2.5 to get the items to work as tribute items. We made the server create a blank item and send the stats of said item out on the wire in the tribute slot. Every time the player changed their gear, we'd send this item back to them.
That wasn't good enough for me. So I researched more...

The UI is now fed stats that are cached from the server. The EQ client does not natively have this functionality, so I had to hijack its existing label functions via a hook. For those uninitiated, hooks allow you to attach to C++ functions at a specific execution point. They 'jump' to the section of code the hook allocates, do a bit of custom logic, and then return to the previous calling function with a new result. They can also use the original function they replace, so it becomes possible (provided you use the same calling convention and parameter types) to add or modify functionality, or even outright replace functionality in-line
Programs like MacroQuest2 are useful for this. However, we don't entirely want to promote the usage of cheat programs and their plugins as a person running a server 'for play'. Also, people are generally uncomfortable with running code they don't know on their machine in the form of executable files. So I came up with a solution to inject the DLL transparently to the end user, while running a modified, but highly stripped down version of MQ2 that allows me to do hooks in the ways I specify.
Traditionally, there would be no way to do this. However, Windows applications have a list of 'exports' and 'imports' associated with the Portable Executable (32-bit or 64-bit assembly) in .exe, dll formats. Exports are listed in any sort of executable. They're essentially a way to tell other applications 'I have this function you need'. Imports are the opposite; they tell programs that you need that function and will refuse to run until you have that function. They can be specified by name or ordinal, and can be used to get any sort of return value. So, of course, EQ has a bunch of imports and exports. Some of them are system libraries, some are provided by third party vendors to support the game.

In 2012, I decided to take my hand at modifying the Titanium game client for Project 1999. The technique was used to implement a fix to disable maps, the advanced tradeskill window, and enable a texture for skeletons that was previously disabled by SOE - I assume to implement the Luclin skeletons, as they were forced to 'texture 0' and the model for classic skeletons was outright replaced by a load order dependency in one of the global character files, so there was good cause to force them to load a specific texture set at some point in development.

However, Project 1999 was not allowed to distribute the game client. This is due to previous Cease and Desist letters from Sony Online Entertainment sent to EQEmulator stating they cannot share the game client or any of its executables or assets. That did not discourage me, and having learned about a DLL injection method from a friend in a for-profit private server community (who ironically works at Riot Games on Vanguard, their anticheat, now) I was able to replace a system DLL named wsock32.dll, which is traditionally used for networking and socket purposes. The code was deployed and it worked - It's virtually transparent to the end user if you include it in an archive and don't know what it does, additionally it has the benefit of allowing code injection without breaking the law by redistributing the program. But antiviruses became wary to the technique, as we were exploiting a technique that malware often uses - replace a system file in a directory and your program runs fine, but also loads the code in the directory and the program ignores the system file entirely. So we had a dilemma once players were finding it detected by AVs, despite being non malicious in code.

Players who knew nothing better thought it was a virus, and short of being tech savvy you can't really convince them otherwise that the code was not a virus and play. So we had to look at alternative avenues - enter the DirectX example code library hijack, where we replaced a file outright that was already in the EQ Titanium folder, DSETUP.dll, which was used to determine if the player had installed DirectX 9 or not. By 2012, virtually every machine was on Vista, 7, or higher and very few were on XP. So this was a golden opportunity to just say 'hey, you have DirectX9' in the function it exports - and also, when the DLL reaches its main entry point even before that (DLLs are loaded and their main entry point is technically a function that tells the program whether it's safe to load or not based on any exceptions or return values) where the program is loaded, but has not reached its own entry point. This allowed us to completely replace the one export in DSETUP.dll with a function that did nothing and allowed the program to continue, while injecting our own code.
However, DSETUP.dll was removed from the import list by 2007, well before I started this hijacking and reversing project, and future projects I did would require the use of newer EQ clients. They were often more reliable and had better options for UI, had the dual core optimization bug fix built in, the west bug fixed by default, etc... they were better clients internally. They ran faster, used less memory, and were generally 'better'. So I did the same technique, but this time with the DirectInput8 library, also from the DirectX SDK. dinput8.dll would be able to be plopped into the folder alongside the install, and would provide the same method of code injection.

I took a copy, as I had mentioned, of MacroQuest for the client in question and embedded it solely contained within the dinput8.dll file. I had to remove the plugin system from MacroQuest as it was no longer needed or relevant to the players in question.

We take a list of stats, send them to the client, and return the value from the server in the label function instead of the return value from the original label function, if the label ID matches the override. It works well albeit from a few edge cases.
// Construct packet opcode = OP_EdgeStats; outapp = new EQApplicationPacket(OP_EdgeStats, 4 + (sizeof(EdgeStatEntry_Struct) * ((int)(eStatEntry::eStatMax) - 1:emoji_nose:; itempacket = (EdgeStat_Struct*)outapp->pBuffer; itempacket->count = (int)(eStatMax) - 1; for(int guava = 0; guava < eStatEntry::eStatMax - 1; guava++) { itempacket->entries[guava].statKey = (eStatEntry)guava; itempacket->entries[guava].statValue = GetStatValueEdgeType((eStatEntry)guava); } QueuePacket(outapp); safe_delete(outapp);
That's how we construct the packet. It loops through all possible values and sends them. That is sent on changing any of the players' stat totals. There's also a smaller one for HP, Mana, and Endurance used.
EmuOpcode opcode = OP_Unknown; EQApplicationPacket* outapp = nullptr; EdgeStat_Struct* itempacket = nullptr; // Construct packet opcode = OP_EdgeStats; outapp = new EQApplicationPacket(OP_EdgeStats, 4 + (sizeof(EdgeStatEntry_Struct) * 2)); itempacket = (EdgeStat_Struct*)outapp->pBuffer; itempacket->count = 2; itempacket->entries[0].statKey = eStatCurHP; itempacket->entries[0].statValue = GetStatValueEdgeType(eStatCurHP); itempacket->entries[1].statKey = eStatMaxHP; itempacket->entries[1].statValue = GetStatValueEdgeType(eStatMaxHP); QueuePacket(outapp); safe_delete(outapp);
Those are sent when the player receives an HP update through another source.
So the game looks like it's working properly to the end user, but fundamentally works a lot different internally.
It's really impressive. And that's only the tip of the iceberg with this system. We've done a lot more and it looks great to the end user. It's a pain to do without the source code, but we do attempt to manage cool stuff here.

Classless 3.0 still has no release date. It's intended to be a complete overhaul, and to have systems I intended to have in from day one on Classless 2.0/2.5 but instead on release. We aren't going to half-ass 3.0, and I've been basically heads-down in code when I work on it and as such communication is minimal and limited to Ailia and Talco, as I am doing a lot of heavy lifting in the client / server. As well as doing my RL job too.

Additionally, I'd like to bring attention to Yawgmoth's new server, Monster Hunter Norrath, which should fill your 'classless'-like itch until 3.0's release. I plan on checking it out, and you should too. It's in no way affiliated with our team... I am more interested in learning from others and encouraging them to try new ideas. There are certain members of our community that are averse to having anything to do with Yawgmoth, and that's fine if you are avoiding it. However, it is a short term option for some players until 3.0 releases and it is there, so I figured I would mention it.

What happened with 2.1, why go straight towards 2.5 and so on? Should I just wait till 3.5 since this is how it works?

I havent played since the first version but I do like the changes youre doing via reading the updates.
 

Secrets

ResetEra Staff Member
1,860
1,856
What happened with 2.1, why go straight towards 2.5 and so on? Should I just wait till 3.5 since this is how it works?

I havent played since the first version but I do like the changes youre doing via reading the updates.

2.5 was a small revamp to the point where some of the changes we wanted to do in 3.0 were in there, but did not warrant a wipe or major changes to previous gameplay were not present to the point of bumping up the 'version'. Quite honestly, it's for the end user. There was never a 2.1 or anything like that; end users are able to understand that '2.5' means it's not a complete rework like 1.0 -> 2.0 was, but the changes were big enough in scope to that adding .5 to the title would indicate that the game had changes since players last played.

We had a full intent of redoing the server if the changes in 2.5 proved to not be enough to sustain interest in the game, both from the developers' perspective and the players' perspective. They weren't from either perspective, in our opinion, so we went back to the drawing board - and thus the full 'version bump' happened - a bump that players can understand without having an in-depth technical explanation about what the version bump means.

This is akin to how Alpha/Closed Beta/Open Beta has been explained to consumers in the past 10-15 years. Alpha normally means 'internal' or 'very exclusive', Closed Beta means 'No information exists about the game publicly but a lot of people are in there giving their feedback', Open Beta has become synonymous with 'Let all the idiots in with no NDA to test the tech holding the servers together', followed by release. No one has to know why it is, but generally consumers have been conditioned that:

alpha = no chance unless knowing someone for entry
closed beta = exclusive access with invites
open beta = try our shit and break it so we can release the game

That same kind of conditioning is what the 2.5 version bump was meant to signal. Pavlov would be proud.
 

Kharzette

Watcher of Overs
4,857
3,473
waz.png
 

Arden

Blackwing Lair Raider
2,632
1,925
2.5 was a small revamp to the point where some of the changes we wanted to do in 3.0 were in there, but did not warrant a wipe or major changes to previous gameplay were not present to the point of bumping up the 'version'. Quite honestly, it's for the end user. There was never a 2.1 or anything like that; end users are able to understand that '2.5' means it's not a complete rework like 1.0 -> 2.0 was, but the changes were big enough in scope to that adding .5 to the title would indicate that the game had changes since players last played.

We had a full intent of redoing the server if the changes in 2.5 proved to not be enough to sustain interest in the game, both from the developers' perspective and the players' perspective. They weren't from either perspective, in our opinion, so we went back to the drawing board - and thus the full 'version bump' happened - a bump that players can understand without having an in-depth technical explanation about what the version bump means.

This is akin to how Alpha/Closed Beta/Open Beta has been explained to consumers in the past 10-15 years. Alpha normally means 'internal' or 'very exclusive', Closed Beta means 'No information exists about the game publicly but a lot of people are in there giving their feedback', Open Beta has become synonymous with 'Let all the idiots in with no NDA to test the tech holding the servers together', followed by release. No one has to know why it is, but generally consumers have been conditioned that:

alpha = no chance unless knowing someone for entry
closed beta = exclusive access with invites
open beta = try our shit and break it so we can release the game

That same kind of conditioning is what the 2.5 version bump was meant to signal. Pavlov would be proud.

Hey Secrets, trying to send you a DM but can't. Can you open a conversation?
 

Flobee

Vyemm Raider
2,579
2,973
I'm looking forward to trying this again. I logged in a week or two ago but my level 1 kept getting raped by every mob I could find. Couldn't make heads or tails of the con system as mobs grey to level 1 ate me just as fast as mobs white at level 1. I was clearly missing something.

I love the idea behind this though so will be back to try again for sure
 

Secrets

ResetEra Staff Member
1,860
1,856
I'm looking forward to trying this again. I logged in a week or two ago but my level 1 kept getting raped by every mob I could find. Couldn't make heads or tails of the con system as mobs grey to level 1 ate me just as fast as mobs white at level 1. I was clearly missing something.

I love the idea behind this though so will be back to try again for sure

Just to let you know we're planning on addressing this during beta.

We're:
1) Making the base # of AA points 1, instead of 0. A single point puts you at a significantly higher power level than a fresh level 1 character with just a weapon.
2) You'll be required to 'spend an AA point' before leaving to your home city.
3) You will need to slot an AC aug (you'll start with one) in your newbie weapon before leaving the tutorial.

This should (hopefully) streamline the way the game is presented to new players.
 
  • 2Like
Reactions: 1 users

Secrets

ResetEra Staff Member
1,860
1,856
Ended up shutting it down. I've also made the game's server code changes and client module available to the public.

I've got to that point where there's too much things to do, but no time to do all of them. Classless was an unfortunate casualty of downsizing responsibilities.
 
  • 3Like
Reactions: 2 users

Secrets

ResetEra Staff Member
1,860
1,856
Ailia and Dingus, two good friends of mine, took over Classless 3's work and have been improving it drastically.

Go check it out if you have time. Link below:
 
  • 1Like
Reactions: 1 user

Gask

Bronze Baron of the Realm
11,752
44,126
  • Charm
    • Charm spells in Classless simply make a copy of the charmed NPC as your pet. The pet then follows all normal pet rules and scaling
    • Charming a monster does not cause the monster to aggro you

So charm is permanent but the NPC's power is scaled down to that of standard pets?
 

Secrets

ResetEra Staff Member
1,860
1,856
  • Charm
    • Charm spells in Classless simply make a copy of the charmed NPC as your pet. The pet then follows all normal pet rules and scaling
    • Charming a monster does not cause the monster to aggro you

So charm is permanent but the NPC's power is scaled down to that of standard pets?
Yes. They also now inherit the class spells of the NPC you spawn.

The current design doc loosely follows this:

 
  • 1Like
Reactions: 1 user