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

01-19-2008, 02:47 AM
|
Hill Giant
|
|
Join Date: Jan 2008
Posts: 102
|
|
Full function-hooks for rules (implemented)
Full function-hooks for rules (implemented)
Hello guys,
I have been fiddling with EQEmu for a couple weeks, and played EQ for several years. And as many world builders I have embarked on a quest of my own : create a custom server, with my own ideas of what the rules should be.
It obviously means customizing the EQEmu emulator code. Which inevitably leads to code merges hair-pulling down the road as soon as you touch code written by the rest of the community who tries both to implement a sane game mechanic and the pristine EQ rules.
I would like to minimize the code merges I will need to make n the future. As much as I would like to get the bug fixes, improved game mechanics etc, I would also like to keep my code for custom rules like evaluating the fizzle chance, hit probability, damage etc. In a way I would like to benefit from advances in "mechanics", while keeping my own "rules".
Immediately the rules mechanism in EQEmu came to mind. Except they are quite limited, as they only allow for tweaking a few numeric values during the processing. You really need more than that when you develop a custom world (don't you?).
May be then it was my professional background calling to me, but I saw only one solution : allow developers to separate the game/server mechanic from the rules. The usual tool : function hooks.
Let's take an example : the zone server needs to execute the casting of a spell by a player. It means graying its spell gems bar, waiting until the cast time is over, computing the channeling chance if an interrupt can occur, check whether a fizzle happened, then finally cast the spell and apply its effects. If you look at the fizzle computation, the fizzle chance computation is right inside the rest of the spell casting code. Wouldn't it be nice that the casting code simply called a function computing the fizzle chance (a percentage), and that this function could be defined by the rule system ?
I thought it would be nice to have this, so I implemented it. It was not a huge indertaking as most of the rules code was there, it just needed to handle one more case : we had integer, real and boolean rules, now we can have function hooks.
Here is how it works :
- the emulator code chooses to call a function hook at a certain point. This has to be coded in by the emulator developer, as part of the processing. At this point the parameters passed to the hook (character level, skill score etc) are decided, as is the result the emulator code will accept (say, the hook can return a float, which is the fizzle chance)
- the world builder has written a function matching the function signature for the hook, and has compiled it in a shared library (Windows DLL, Unix .so). The library is stored at the same place as the other server executables.
- the world builder has created the appropriate rule in its database, with a value like "library:function", library being the shared library name, function being the hook function the server must call
- the emulator code will call the rules sub-system, who will load the dynamic library and provide the hook(ed) function pointer to the emulator code
- the emulator code calls the function that implements the rule
If the world builder did not define the rule, the default hook is called. It contains the exact code the emulator server currently imbeds in its processing (that is, this code is moved from its current place to a different file in "common", and is the default implementation).
I could successfully test the implementation of this mechanic on Windows and Gentoo Linux. On Windows it relies on the regular LoadLibrary and GetProcAddress APIs. On Linux it uses the functions dlopen and dlsym of library libdl, which should be common on most Linux installations nowadays (I think it originates from FreeBSD, but I could be wrong). For other platforms I have no way to write/test the code so someone would have to take over. The shared library loading itself is pretty much isolated in one "case" branch in rulesys.cpp, so it's only a few lines of code to adapt.
There is one limitation of the implementation, due to how the rules system is implemented : you cannot dynamically save the hook setting to the database. The reason is that the hook is a function pointer, whereas in the database the library name and the function names are stored. This information is not avaiable in the Save function. I can live with that, but someone interested could probably add this functionality ot the rules system.
Last but not least, here is the code to make all this work. I hope it ends up in the official emulator in the end, it would ease my task  And, given some time to replace hard-wiring rules into the code by well-thought function hooks, it could make the life of custom world builders easier.
I wanted to add a ZIP file to this post with the CVS DIFF file and the two new files, but it seems I do not ave the permissions. So you will have to suffer the inclusion of the files contents directly in the post ! And as it is too big to fit in one post, you will even get several !
The two files are a header file and an implementation file for the default rules implementation. They go to the "common" directory. Most of the contents are comments.
|
 |
|
 |
 |
|
 |

01-19-2008, 02:50 AM
|
Hill Giant
|
|
Join Date: Jan 2008
Posts: 102
|
|
Cvs Diff part 1
Code:
? output.diff
? Source/common/default_Hooks.cpp
? Source/common/default_Hooks.h
Index: Source/common/rulesys.cpp
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/common/rulesys.cpp,v
retrieving revision 1.2.2.2
diff -u -b -B -r1.2.2.2 rulesys.cpp
--- Source/common/rulesys.cpp 31 Oct 2006 02:02:54 -0000 1.2.2.2
+++ Source/common/rulesys.cpp 19 Jan 2008 13:37:36 -0000
@@ -20,7 +20,10 @@
#include "logsys.h"
#include "database.h"
#include "MiscFunctions.h"
-
+#ifndef WIN32
+/* On Linux, include the headers for libdl for dynamic library loading */
+#include <dlfcn.h>
+#endif /* !WIN32 */
/*
FatherNitwit: Added new rules subsystem to allow game rules to be changed
@@ -73,7 +76,7 @@
"InvalidCategory"
};
-const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount+1] = {
+const RuleManager::RuleInfo RuleManager::s_RuleInfo[_IntRuleCount+_RealRuleCount+_BoolRuleCount+_HookRuleCount+1] = {
/* this is done in three steps so we can reliably get to them by index*/
#define RULE_INT(cat, rule, default_value) \
{ #cat ":" #rule, Category__##cat, IntRule, Int__##rule },
@@ -84,6 +87,9 @@
#define RULE_BOOL(cat, rule, default_value) \
{ #cat ":" #rule, Category__##cat, BoolRule, Bool__##rule },
#include "ruletypes.h"
+ #define RULE_HOOK(cat, rule, default_value) \
+ { #cat ":" #rule, Category__##cat, HookRule, Hook__##rule },
+ #include "ruletypes.h"
{ "Invalid Rule", _CatCount, IntRule }
};
@@ -149,6 +155,56 @@
m_RuleRealValues[index] = atof(rule_value);
_log(RULES__CHANGE, "Set rule %s to value %.13f", rule_name, m_RuleRealValues[index]);
break;
+ case HookRule:
+ { char *fLibraryName = NULL, *fFunctionName = NULL;
+ Hook ThisHook = NULL;
+ const char *Colon = strchr(rule_value, ':');
+ if(Colon == NULL)
+ _log(RULES__ERROR, "Badly-formed hook value '%s' (rule '%s'). It must contain a colon (:). Ignoring this hook", rule_value, rule_name);
+ else
+ { fLibraryName = (char *) malloc(Colon - rule_value + 128 + 1); /* 128 = let's assume it is more than enough to append a file extension to the library if needed */
+ strncpy(fLibraryName, rule_value, Colon - rule_value); fLibraryName[Colon - rule_value] = '\0';
+ fFunctionName = (char *) malloc(strlen(rule_value) - (Colon - rule_value + 1) + 1);
+ strcpy(fFunctionName, Colon + 1);
+ }
+#ifdef WIN32
+ HMODULE ThisLibrary = NULL;
+ if(fLibraryName != NULL)
+ { strcat(fLibraryName, ".dll");
+ ThisLibrary = LoadLibrary(fLibraryName);
+ if(ThisLibrary == NULL)
+ _log(RULES__ERROR, "Cannot load library '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fLibraryName, rule_value, rule_name);
+ }
+
+ if(ThisLibrary != NULL)
+ { ThisHook = GetProcAddress(ThisLibrary, fFunctionName);
+ if(ThisHook == NULL)
+ _log(RULES__ERROR, "Cannot load library function '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fFunctionName, rule_value, rule_name);
+ }
+#else /* WIN32 */
+ void *ThisLibrary = NULL;
+ if(fLibraryName != NULL)
+ { strcat(fLibraryName, ".so");
+ ThisLibrary = dlopen(fLibraryName, RTLD_NOW | RTLD_GLOBAL);
+ if(ThisLibrary == NULL)
+ _log(RULES__ERROR, "Cannot load library '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fLibraryName, rule_value, rule_name);
+ }
+
+ if(ThisLibrary != NULL)
+ { ThisHook = dlsym(ThisLibrary, fFunctionName);
+ if(ThisHook == NULL)
+ _log(RULES__ERROR, "Cannot load library function '%s' needed by hook '%s' (rule '%s'). Ignoring this hook", fFunctionName, rule_value, rule_name);
+ }
+ /*_log(RULES__ERROR, "The rule hooks are not yet implemented on this platform. Ignoring hook '%s' for fule '%s'", rule_value, rule_name);*/
+#endif /* WIN32 */
+ if(ThisHook != NULL)
+ { m_RuleHookValues[index] = ThisHook;
+ _log(RULES__CHANGE, "Set rule %s to value %d", rule_name, (int) m_RuleHookValues[index]);
+ }
+ if(fLibraryName != NULL) { free(fLibraryName); fLibraryName = NULL; }
+ if(fFunctionName != NULL) { free(fFunctionName); fFunctionName = NULL; }
+ break;
+ }
case BoolRule:
bool val = false;
if(!strcasecmp(rule_value, "on") || !strcasecmp(rule_value, "true") || !strcasecmp(rule_value, "yes") || !strcasecmp(rule_value, "enabled") || !strcmp(rule_value, "1"))
@@ -172,6 +228,8 @@
m_RuleRealValues[ Real__##rule ] = default_value;
#define RULE_BOOL(cat, rule, default_value) \
m_RuleBoolValues[ Bool__##rule ] = default_value;
+ #define RULE_HOOK(cat, rule, default_value) \
+ m_RuleHookValues[ Hook__##rule ] = default_value;
#include "ruletypes.h"
}
@@ -202,6 +260,8 @@
return(s_RuleInfo[index+_IntRuleCount].name);
case BoolRule:
return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount].name);
+ case HookRule:
+ return(s_RuleInfo[index+_IntRuleCount+_RealRuleCount+_BoolRuleCount].name);
}
//should never happen
return("InvalidRule??");
@@ -236,6 +296,9 @@
for(r = 0; r < _BoolRuleCount; r++) {
_SaveRule(db, BoolRule, r);
}
+ for(r = 0; r < _HookRuleCount; r++) {
+ _SaveRule(db, HookRule, r);
+ }
}
@@ -280,7 +343,10 @@
void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) {
char vstr[16];
- switch(type) {
+ if(type == HookRule)
+ _log(RULES__ERROR, "Impossible to save hook rule %s in the database. Hook rule saving is not supported", _GetRuleName(type, index));
+ else
+ { switch(type) {
case IntRule:
sprintf(vstr, "%d", m_RuleIntValues[index]);
break;
@@ -302,6 +368,7 @@
_log(RULES__ERROR, "Fauled to set rule in the database: %s: %s", query,errbuf);
}
safe_delete_array(query);
+ }
}
Index: Source/common/rulesys.h
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/common/rulesys.h,v
retrieving revision 1.2.2.2
diff -u -b -B -r1.2.2.2 rulesys.h
--- Source/common/rulesys.h 31 Oct 2006 02:02:54 -0000 1.2.2.2
+++ Source/common/rulesys.h 19 Jan 2008 10:07:13 -0000
@@ -35,6 +35,8 @@
rules->GetRealRule( RuleManager::Real__##rule )
#define RuleB(cat, rule) \
rules->GetBoolRule( RuleManager::Bool__##rule )
+#define RuleH(cat, rule) \
+ rules->GetHookRule( RuleManager::Hook__##rule )
#include <vector>
@@ -42,6 +44,7 @@
#include <map>
#include "types.h"
+#include "../common/default_Hooks.h"
class Database;
@@ -70,6 +73,13 @@
} BoolType;
typedef enum {
+ #define RULE_HOOK(cat, rule, default_value) \
+ Hook__##rule,
+ #include "ruletypes.h"
+ _HookRuleCount
+ } HookType;
+
+ typedef enum {
#define RULE_CATEGORY(catname) \
Category__##catname,
#include "ruletypes.h"
@@ -79,9 +89,10 @@
static const IntType InvalidInt = _IntRuleCount;
static const RealType InvalidReal = _RealRuleCount;
static const BoolType InvalidBool = _BoolRuleCount;
+ static const HookType InvalidHook = _HookRuleCount;
static const CategoryType InvalidCategory = _CatCount;
- static const uint32 _RulesCount = _IntRuleCount+_RealRuleCount+_BoolRuleCount;
+ static const uint32 _RulesCount = _IntRuleCount+_RealRuleCount+_BoolRuleCount+_HookRuleCount;
RuleManager();
@@ -89,11 +100,13 @@
inline int GetIntRule (IntType t) const { return(m_RuleIntValues[t] ); }
inline float GetRealRule(RealType t) const { return(m_RuleRealValues[t]); }
inline bool GetBoolRule(BoolType t) const { return(m_RuleBoolValues[t]); }
+ inline Hook GetHookRule(HookType t) const { return(m_RuleHookValues[t]); }
//management routines
static const char *GetRuleName(IntType t) { return(s_RuleInfo[t].name); }
static const char *GetRuleName(RealType t) { return(s_RuleInfo[t+_IntRuleCount].name); }
static const char *GetRuleName(BoolType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount].name); }
+ static const char *GetRuleName(HookType t) { return(s_RuleInfo[t+_IntRuleCount+_RealRuleCount+_BoolRuleCount].name); }
static uint32 CountRules() { return(_RulesCount); }
static CategoryType FindCategory(const char *catname);
bool ListRules(const char *catname, std::vector<const char *> &into);
@@ -117,11 +130,13 @@
int m_RuleIntValues [_IntRuleCount ];
float m_RuleRealValues[_RealRuleCount];
bool m_RuleBoolValues[_BoolRuleCount];
+ Hook m_RuleHookValues[_HookRuleCount];
typedef enum {
IntRule,
RealRule,
- BoolRule
+ BoolRule,
+ HookRule
} RuleType;
static bool _FindRule(const char *rule_name, RuleType &type_into, uint16 &index_into);
Index: Source/common/ruletypes.h
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/common/ruletypes.h,v
retrieving revision 1.3.2.8
diff -u -b -B -r1.3.2.8 ruletypes.h
--- Source/common/ruletypes.h 21 Feb 2007 16:04:19 -0000 1.3.2.8
+++ Source/common/ruletypes.h 19 Jan 2008 12:42:10 -0000
@@ -13,6 +13,9 @@
#ifndef RULE_BOOL
#define RULE_BOOL(cat, rule, default_value)
#endif
+#ifndef RULE_HOOK
+#define RULE_HOOK(cat, rule, default_value)
+#endif
#ifndef RULE_CATEGORY_END
#define RULE_CATEGORY_END()
#endif
@@ -71,10 +74,16 @@
RULE_REAL ( Combat, ClientBaseCritChance, 0.0 ) //The base crit chance for all clients, this will stack with warrior's/zerker's crit chance.
RULE_CATEGORY_END()
+RULE_CATEGORY( CharacterCreation )
+RULE_HOOK ( CharacterCreation, ChangeCreationInfo, (Hook) default_CharacterCreation_ChangeCreationInfo )
+RULE_CATEGORY_END()
+
+
#undef RULE_CATEGORY
#undef RULE_INT
#undef RULE_REAL
#undef RULE_BOOL
+#undef RULE_HOOK
#undef RULE_CATEGORY_END
|
 |
|
 |
 |
|
 |

01-19-2008, 02:51 AM
|
Hill Giant
|
|
Join Date: Jan 2008
Posts: 102
|
|
CVS DIFF Part 2
Code:
Index: Source/world/client.cpp
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/world/client.cpp,v
retrieving revision 1.12.2.20
diff -u -b -B -r1.12.2.20 client.cpp
--- Source/world/client.cpp 22 Nov 2006 14:03:34 -0000 1.12.2.20
+++ Source/world/client.cpp 19 Jan 2008 09:19:57 -0000
@@ -907,6 +907,8 @@
return false;
}
+ ((Hook_CharacterCreation_ChangeCreationInfo) RuleH(CharacterCreation, ChangeCreationInfo))(cc);
+
// Convert incoming cc_s to the new PlayerProfile_Struct
memset(&pp, 0, sizeof(PlayerProfile_Struct)); // start building the profile
Index: Source/world/makefile.common
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/world/makefile.common,v
retrieving revision 1.1.2.6
diff -u -b -B -r1.1.2.6 makefile.common
--- Source/world/makefile.common 31 Oct 2006 02:02:54 -0000 1.1.2.6
+++ Source/world/makefile.common 19 Jan 2008 13:37:36 -0000
@@ -24,7 +24,7 @@
../common/SocketLib/HttpdForm.o ../common/SocketLib/HttpdSocket.o \
../common/SocketLib/MemFile.o ../common/SocketLib/Mime.o \
../common/SocketLib/Parse.o ../common/SocketLib/Utility.o \
- ../common/guild_base.o wguild_mgr.o ../common/rulesys.o
+ ../common/guild_base.o wguild_mgr.o ../common/rulesys.o ../common/default_Hooks.cpp
all: $(APP)
Index: Source/zone/makefile.common
===================================================================
RCS file: /cvsroot/eqemulator/EQEmuCVS/Source/zone/makefile.common,v
retrieving revision 1.2.2.12
diff -u -b -B -r1.2.2.12 makefile.common
--- Source/zone/makefile.common 18 Jul 2006 05:04:49 -0000 1.2.2.12
+++ Source/zone/makefile.common 19 Jan 2008 13:37:36 -0000
@@ -30,7 +30,7 @@
../common/EQStreamIdent.o ../common/patches/Live.o \
zone_logsys.o ../common/BasePacket.o ../common/worldconn.o \
../common/EmuTCPConnection.o ../common/EmuTCPServer.o ../common/TCPServer.o \
- ../common/guild_base.o guild_mgr.o
+ ../common/guild_base.o guild_mgr.o ../common/default_Hooks.cpp
all: $(APP)
|
 |
|
 |
 |
|
 |

01-19-2008, 02:52 AM
|
Hill Giant
|
|
Join Date: Jan 2008
Posts: 102
|
|
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).
|
 |
|
 |
 |
|
 |

01-19-2008, 03:19 AM
|
Hill Giant
|
|
Join Date: Jan 2008
Posts: 102
|
|
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 
|
 |
|
 |

01-21-2008, 08:59 PM
|
Administrator
|
|
Join Date: Sep 2006
Posts: 1,348
|
|
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!
|
 |
|
 |

01-22-2008, 11:53 AM
|
Hill Giant
|
|
Join Date: Jan 2008
Posts: 102
|
|
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 !
|
 |
|
 |
 |
|
 |

02-28-2008, 08:57 AM
|
Hill Giant
|
|
Join Date: Jan 2008
Posts: 102
|
|
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.
|
 |
|
 |
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -4. The time now is 05:10 AM.
|
|
 |
|
 |
|
|
|
 |
|
 |
|
 |