Go Back   EQEmulator Home > EQEmulator Forums > Development > Development::Development

Development::Development Forum for development topics and for those interested in EQEMu development. (Not a support forum)

Reply
 
Thread Tools Display Modes
  #1  
Old 01-19-2008, 02:52 AM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default Two files that go in "common"

Code:
common/default_Hooks.h
----------------------
#ifndef DEFAULT_RULES_H_
#define DEFAULT_RULES_H_

#include "../common/eq_packet_structs.h"

/** A hook can be a function pointer of any type. So we define it as void *.
 *  Hooks are cast to their actual function type when they are called.
 */
typedef void *Hook;

/** Macro used to check the compliance of hook functions with the definition of the hook.
 *  It ensures the hook function has the correct signature.
 *  It can be very useful to avoid mistakes, and when a hook signature changes.
 *  The compiler can then issue an error.
 *  If no signature check is done, a badly-defined hook function will probably crash the server.
 *  usage: add a line after the hook function definition (in the *.cpp file)
 *    CheckHookSignature(CharacterCreation, ChangeCreationInfo, default_CharacterCreation_ChangeCreationInfo);
 */

#define CheckHookSignature(cat, rule, hook) \
	static Hook_##cat##_##rule CheckSignature_##hook = hook


/** Called during character creation right after the CharCreate structure has been checked for correctness.
 *  Allows the world builder to tweak the values sent by the client prior to the actual character creation.
 */
typedef void (*Hook_CharacterCreation_ChangeCreationInfo)(CharCreate_Struct *cc);


/** Does nothing.
 */
void default_CharacterCreation_ChangeCreationInfo(CharCreate_Struct *cc);


#endif /*DEFAULT_RULES_H_*/



common/default_Hooks.cpp
------------------------
#include "../common/debug.h"
#include "../common/default_Hooks.h"


void default_CharacterCreation_ChangeCreationInfo(CharCreate_Struct *cc)
{
};
CheckHookSignature(CharacterCreation, ChangeCreationInfo, default_CharacterCreation_ChangeCreationInfo);


I did not include the changes to the VC++ projects, I am kinda scared about the result. I will just give directives : add files common/default_Hooks.h and common/default_Hooks.cpp to the Common header/Source files of the World and the Zone projects

I will make a reply to this post to show how to implement the one hook I placed in the emulator code at the moment (a hook to tweak the character info structure coming from the Everquest client, prior to the actual character creation).
Reply With Quote
  #2  
Old 01-19-2008, 03:19 AM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default How to implement the Character Creation hook

To ensure the rules hook worked I included one hook, during the character creation. Why there ? Because I had problems running "zone" under Linux (my installatio's fault most certainly), so I chose one place I would easily access in "world". Plus I want to make everyone a ranger !
This hook is inserted into the character creation process, right after the server checks for cheaters (it checks whether the stats provided are possible), and before the character is actually created. Now that we are sure the guy at the other end is not cheating, we can change his character how we see fit.
This hook function signature is simple : it receives the CharInfo_Struct pointer and can fiddle with it however it wants. The function returns nothing, it works by altering the CharInfo_Struct. The hook signature is :
Code:
void default_CharacterCreation_ChangeCreationInfo(CharCreate_Struct *cc);
The default implementation does nothing. The character is created as the player asked.
My Hero server forces him to be a ranger !
To implement the hook (the first hook is of course the longest), you must create a new project for your rules shared library. On Windows I created a new project named "Hero", and made every single setting identical to the EmuSharedMem project, as I was not sure how to create a proper DLL. On Unix, you also make a new directory and copy in the makefile and makefile.common files from EmuSharedMem, that you adapt to your library (rename EmuSharedMem to Hero in my case).
You then need one hero_Hooks.cpp file for your custom hook, and on Windows you need an additional DLLMain.cpp file. You can copy the one from EmuSharedMem, it works fine.

Here is the contents of the hero_Hooks.cpp file. It ensures the function is exported as a DLL on Windows, implements the hook signature, and makes the character a ranger !
Code:
#include "../common/debug.h"
#include "../common/eq_packet_structs.h"
#include "../common/default_Hooks.h"
#include "../common/classes.h"

#ifdef WIN32
	#define exportfunc extern "C" __declspec(dllexport)
#else
	#define exportfunc extern "C"
#endif

/** Makes all newly-created characters Rangers - It is the class chosen for heroes as it can cast spells
 *  and have dual-wield. After all, aren't all rangers heroes ?
 */
exportfunc void hero_CharacterCreation_ChangeCreationInfo(CharCreate_Struct *cc)
{	cc->class_ = RANGER;
};
CheckHookSignature(CharacterCreation, ChangeCreationInfo, hero_CharacterCreation_ChangeCreationInfo);
Note that the CheckHookSignature macro call is not mandatory, but it will warn you in case the hook signature changes (the compiler will report an error).

Then you compile, copy the Hero.[dll|so] library file with his friends, the world and zone executable in your server directory, and you run your server. Guess what, nothing has changed (yet).

You need to tell the emulator that you want to use this new hook for your ruleset. If you have never played with rules before what's coming next will be rough, but I will not try to explain how rules work. You can certainly find this information on the site.

Run the following commands against your database to enable the new hook :
Code:
ALTER TABLE rule_values MODIFY rule_value VARCHAR(128);

INSERT INTO rule_sets values ('2', 'hero');

INSERT INTO rule_values VALUES ('2','CharacterCreation:ChangeCreationInfo','Hero:hero_CharacterCreation_ChangeCreationInfo');

INSERT INTO variables(varname, value, information) VALUES ('RuleSet', 'hero', 'Rule set currently in use for this server');
Note that it will alter your database to allow for long rule values. You could very well stay with the old rule values column size, but you will be forced to use very short library/function names, so that the value can fit in the column. Yes, that means 9 characters total for the library plus the function name (the column is size 10, and you have to include a colon in there). If you do not want to alter your database, rename the function something like "h001", and use "Hero:h001" for the rule value in the query above. It should work fine.

You need to restart (or start) your world server for the rule to be re-read from the database. Once your world server is up, fire up the EQ client and create a new character, preferably not a ranger. You will get a ranger anyway, even if your race does not allow it.

I hope you like rangers
Reply With Quote
  #3  
Old 01-21-2008, 08:59 PM
KLS
Administrator
 
Join Date: Sep 2006
Posts: 1,348
Default

I'll see if I can't point FNW at this when I can, I don't think my level of expertise really covers this kind of thing. Don't get discouraged!
Reply With Quote
  #4  
Old 01-22-2008, 11:53 AM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default

I have been starting to add a couple hooks for my own use, used in the "zone" process. That is when I realized that you do not want to mix hooks for "zone" and hooks for "world" at the same time. Doing so would pull unwanted files (to "world" mostly), like "mob.cpp", if you need to include "mob.h" to implement your hook.

Of course there is a solution

This solution requires a minor change in "ruletypes.h" to include some #ifdef directives, to only compile hooks for "world" when you compile world, and vice-versa.
It also requires some #ifdef in "default_Hooks.h" and "default_Hooks.cpp" to do the same. As the file is new I can probably just dump a new version.
Finally it requires building not one shared library for the custom hooks, but two : one to be loaded by "world", one to be loaded by "zone".

I still have a remaining issue with warnings appearing when "world" and "zone" load, because both processes try to load ALL the rules, not just those pertaining to them. I can probably find a solution by this week-end but right now I am going to bed

To sum it up, I should post the fixed version around this week-end. Nothing outstanding changed, but some more #ifdef to cleanly separate things will be added.

So I keep my hopes high !
Reply With Quote
  #5  
Old 02-28-2008, 08:57 AM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default

I am sorry for not answering earlier but work has been quite "entertaining" lately, and my time on EQEmu went low as a consequence. Still I worked some more on this topic (and on server/quest implementation) and found out that it was not as straightforward as expected, as usual with computer engineering.

The major problem I encountered is that you cannot pass just any argument you like to the hook functions (like a Client instance) and use it, as otherwise you would drag all the rest of the cpp files with your hooks. This limits the scope of the hooks.

The second problem (a consequence of the previous one) is that you do not want to move the existing behaviour out of the current functions. Instead it is easier to define the hooks as 'NULL' by default, and only call them if they are set in the rules, with the list of real parameters you need (ability scores etc) not the full objects containing them. A simple "if" does the trick at the point where the hook is introduced.

In the end it proved usable, and useful for me. I could also work-around the problem with hooks specific to "world" and "zone" by putting some prefix in the rule values.

Before submitting again what would be the final version I need to merge the new code written for EQRmu since my first submission. Hopefully I can do it this week-end. It will also show me whether implementing these hooks helped in maintaining parallel code for custom rules, while keeping compatibility with changes done on EQEmu as a whole.
Reply With Quote
  #6  
Old 02-28-2008, 11:03 PM
trevius's Avatar
trevius
Developer
 
Join Date: Aug 2006
Location: USA
Posts: 5,946
Default

Sounds interesting! I would love to have the ability to have more custom rules. At the very least, it sounds like Bulle could be a potential candidate for helping on the Emu dev team

I believe the devs have been working to add more rules in the future. I read all of these posts, but I am not much of a coder, so I don't fully understand how hooks differ from the normal way that KLS, Wildcardx, FNW and the team have been adding new rules. To me, it seems like hooks are just another way to add more custom rules. I am not sure how the devs currently do it, but their way seems to work well. As long as everything works in the end code, I am happy though, lol.
__________________
Trevazar/Trevius Owner of: Storm Haven
Everquest Emulator FAQ (Frequently Asked Questions) - Read It!
Reply With Quote
  #7  
Old 02-29-2008, 07:22 AM
Bulle
Hill Giant
 
Join Date: Jan 2008
Posts: 102
Default

To put it simply, the difference between a rule and a hook is how you write it, and hence the power of it.

A rule is merely a number (simplifying here) allowing you to tweak a pre-set behaviour, which a developer has coded. It requires no special skill to use except understanding what the number's impact is.

A hook is a portion of code (let's say 10 lines long) that is called instead or in addition of a regular server computation. It requires you to know how to code a C++ function, but it is much more powerful. In the spirit it is like a short script, just it is in C++, so you need to compile the code yada yada.

Best is to use an example : resist chance for a spell.
Current rules allow you to specify a resist base percentage (ResistChance), a resist multiplier (ResisstMod), a level difference after which no resist is possible for the PC (AutoResistDiff) and the proportion of partial resists (PartialHitchance). The behaviour using these variables is written for you, you cannot change it.
On my test server, instead, I allowed replacing the full resist chance computation with a hook. By defining the hook and implementing in C++ as a function accepting a few parameters and returning the resist chance, I can compute it as I want. In my case I do something on the line of the Shards of Dalaya server, the CHA of the caster influences the resist chance (and the WIS of the target decreases it).

Why not just replace the existing server code ? Because keeping the changes to the original server code is important if you want to integrate new changes done on the main server easily in your custom server. Once the hook is in place (and it can be put in the main server code as by default it changes nothing to the "live-like" server behaviour) custom server developers do not need to touch the original code to significantly tune some rules.

That was my goal. I wanted to implement a score of custom rules, but still keep my server "close to original" EQEmu. In the end I could do most of my changes with hooks and implemented :
- overridding some player choices during character creation
- rewriting the resist chance for spells
- rewriting the melee to-hit chance, damage, mitigation
- rewriting the mana regen
- rewriting the fizzle chance
- rewriting the AC computation (the client cannot show the server-used score, too bad but inevitable)
- rewriting the EXP per level

With minimal and not disrupting changes to the current server code.

I hope it helped explain the difference between rule variables, hooks, and utter server code change. The idea is that hooks could be part of the regular server code "as options ready to be used", on the developer choice, and without having such checks like "if server is guild wars". It would be instead : if designer wants something special we call it, otherwise "live" implementation.
Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump

   

All times are GMT -4. The time now is 10:37 PM.


 

Everquest is a registered trademark of Daybreak Game Company LLC.
EQEmulator is not associated or affiliated in any way with Daybreak Game Company LLC.
Except where otherwise noted, this site is licensed under a Creative Commons License.
       
Powered by vBulletin®, Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Template by Bluepearl Design and vBulletin Templates - Ver3.3