drewlt
Mar 13 2006, 01:36 PM
So I've been checking this out the last few weeks trying to figure out how it works. Finally decided to ask for help. Anything you can give me would be helpful.

Trying to write my own program to write values to the MMF to pull into ACTools using {PluginResult}.
Been looking at the ScriptBridge files and this post on ffact boards -
http://www.ffact.botanybay.net/forums/inde...p?showtopic=402 - this looks like it might be useful but the links are dead.
The ScriptBridge code gives me a lot and I'm sure it's right in front of me, but mmf's are new to me and I am just not getting it.
So - Questions:
- How do I inject my own data into the mmf.
- Is the mmf just an array of the TYPE that is defined? (Function name, arguments, return value, etc...)
- When GetAttr is called, does it just read data from the mmf (so would the exe constantly need to update the mmf) or does it call a function (dunno how it would).
Sample code is welcome.
Thanks

-DT
Ipa
Mar 13 2006, 02:49 PM
| CODE |
- How do I inject my own data into the mmf.
|
You need to use the Win32 API calls that write to MMF's. You then need to deal with the added complexity of mapping VB6 datatypes (and their in-memory representation) to Delphi equivalents.
| CODE |
| Is the mmf just an array of the TYPE that is defined? |
Every programming language has something equivalent to VB6's "TYPE". In C or C++, you have "structs". In Delphi you have "packed records".
Essentially these are compound user defined data types, composed of each language's primitive data types.
We're not dealing with arrays here, just 1 variable of a certain TYPE (on the VB6 side) or a packed record on the Delphi side.
Using a very small example of a subset of the full MMF "packed record" illustrating just the 1st 3 elements
Delphi
| CODE |
TGlobal = packed record InstanceCount: Cardinal; Command: Integer; Param1: ShortString;
|
We're defining something, and calling it a "tGlobal". We're declaring that this new data type has 3 elements. In order to create a VB6 equivalent, we need to do some research into how a Delhi "Cardinal", "Integer" and "ShortString" map to VB6.
Since I've already done that research and had fun with all manner of crashes & errors, I can save you some time.
VB6
| CODE |
Public Type tGlobal InstanceCount As Long Command As Long Param1(255) As Byte 'Note - VB always adds 1 extra array element, hence 255 instead of 256
|
The only complication really is the Delphi ShortString - it is a 256 byte datatype, where byte 1 (or zero, if you're a zero-based thinker) indicates the length of the string.
So now you're good to go - you've defined a 4 + 4 + 256 byte "type" where the 264 bytes on each side map correctly to each other. in the different languages.
Problem - ACTool's tGlobal type is huge, and contains a whole bunch of crap that is specific only to Asheron's Call. Best way around that is to just declare a big padding element to skip over to what's of interest for FFXI.
Tada:
| CODE |
'This should correspond to the Delphi TGlobal packed record type in ACTool 'and must be kept in sync with any changes to the Delphi type 'See ACTool source code file "memlocs.pas" Public Type tGlobal InstanceCount As Long Command As Long Param1(255) As Byte 'Note - VB always adds 1 extra array element, hence 255 instead of 256 Param2(255) As Byte Param3(255) As Byte Param4(255) As Byte Param5(255) As Byte Performed As Byte Result(255) As Byte Result2(255) As Byte Padding(10185) As Byte 'This pads over the Asheron's Call 1 variables RunMacro(255) As Byte ActCmdEvent(255) As Byte ActCmdParam(255) As Byte PromptAC_LoByte As Byte PromptAC_HiByte As Byte p1 As Byte p2 As Byte MacroStatus As Byte ACToolHandle As Long PluginVersion(255) As Byte End Type |
| QUOTE |
| - When GetAttr is called, does it just read data from the mmf |
GetAttr is an ACTool command that was suppsoed to be for Asheron's Call 1, but which is also being used for FFACT. When an ACTool command like this runs, it puts data into the MMF. It expects the ACTool "Companion" to act on this data and write some stuff back to the MMF when it's done.
What FFACT does is "pretend" to be ACTool Companion. Therefore to code any extensions using the MMF, you just need to mimic Companion. Which means you need to mimic the protocol.
ACTool requests something by:
Setting MMF.Command
Setting MMF.Performed to zero
Companion (or your FFACT mimic) works by:
Polling MMF.Command on a regular basis, looking for non-zero
If non-zero, acting on the command
Once complete, setting MMF.Performed to non-zero, setting MMF.Command to zero and sending stuff back to ACTool in MMF.Result (which ACTool then gives to your macro as {PluginResult})
DaMOB
Mar 13 2006, 03:22 PM
The only complication really is the Delphi ShortString - it is a 256 byte datatype, where byte 1 (or zero, if you're a zero-based thinker) indicates the length of the string.
Oh, god a pascal string.
Don't get me wrong I think Pascal strings are faster than C strings because you are implicitly tell me how long this freaking thing is instead of having to scan the string until nul is found to figure out how long it is.
C xxxxxxxxx.nul
Pascal lgth.xxxxxxxxx
On really slow machines (aka the old days) a pascal string would blow away C in speed.
VB to Delphi to VB makes my skin crawl so good luck.
Ipa
Mar 13 2006, 03:26 PM
Attached is what "ScriptBridge" evolved into. Fairly generic VB6 component that can be used to interface just about anything to ACTool's MMF as long as it supports COM.
drewlt
Mar 13 2006, 04:42 PM
Excellent, thanks Ipa
Let me read this back to see if I get it.
- The MMF is just one tGlobal instance.
- The entire tGlobal structure needs to be defined (ffact post only defines the first few items - is this not cool?)
- GetAttrib sets the .command to _________ Integer? I see ffact sends a string from actools - I see companion uses integers, but ffact does not.
----- Also sets the .performed to 0
My program would monitor the (edit) .command and when it is set to non-zero, perform calculations and set .performed to non-zero, .command to 0, and .result (and .result2 ?) to what I want returned into {PluginResult}
So, only question before I start with this is what (datatype) to pass to the .command from actools and what my program will see. I can also just start messin with this to see.

-DT
edit: Thanks for the attachment - that may answer my Q's
drewlt
Mar 21 2006, 03:10 PM
So here's my vb.net class to interact with actools. It would have been nice to be able to use CopyMemory or some other quick conversion into a .net structure, but I just could not get it to work. Ended up mapping the file and pulling in individual variables.
Problems I had to bypass:
-- Unable to map a structure from the mmf pointer
-- Unable to pull in more than 4096 bytes into a byte array using Marshal.Copy
It's functional and it's ok, but if you see any improvements, I'd be interested. I'll build the rest of the app around this and then drop the project, but I think I'll still be interested in this for a while.

-DT
DaMOB
Mar 21 2006, 04:13 PM
No offense but I fucking hate .net shit.
Yeah, I am very set in my ways and biased like a motherfucker against .net and c#.
Good job though.
drewlt
Mar 21 2006, 04:31 PM
Understood, and if I knew more about how programs actually work I'd probably agree (sounds like the general consensus out there). I've picked up books here and there and I do intend one day to get into the C++ world as more and more of the work i do demands a more thorough understanding of programs and how they interact.
'Make it work well' is becomming more important than 'make it work.'
For another day...

-DT
Ipa
Mar 21 2006, 05:07 PM
Nice work.
Dealing with MMF's in .Net and VB6 will always be less than optimal due to lack of true pointers.
Delphi and C/C++ will allow you to have reference type constructs where writing a value to the variable is actually writing to the bytes in the underlying MMF which will always be faster - and more prone to nasty crashes or wierd bugs if you don't know what's going on and aren't coding to allow for concurrency issues with both sides writing to the MMF at the same time.
DaMOB
Mar 21 2006, 05:29 PM
Ipa is right and that extra layer, or lack of one, is supposedly .net's strong suit because it allows any ole monkey to program without fear or a need to know everything. The cost of the lack of knowledge on the programmers part is a reduction in code speed.
As a business they want it fast and they want it from the lowest paid programmer they can get and .net/c# fills that niche quite readily but I am old school. I am so old school I count my clock cycles and how many bytes the program takes up on disc and in memory (old habits die hard) but I know when my program is done it is as fast as I can make it. Of course the Bill Gates types simply say to go out and buy faster hardware. Pfffft to that.
Still though good job on getting it to work. Now make it faster, leaner and meaner.
Oh, and .net-less.
drewlt
Apr 19 2006, 02:00 PM
Got a new one - is there a way (without modifying actool source) to have an external application request a TimeStamp command from ACTool? Through MMF or windows messages or otherwise?
Like to possibly log messages from my app into ACTool if it's not too much work.

-DT
cgrinds
Apr 19 2006, 05:39 PM
| QUOTE (drewlt @ Apr 19 2006, 01:00 PM) |
Got a new one - is there a way (without modifying actool source) to have an external application request a TimeStamp command from ACTool? Through MMF or windows messages or otherwise?
Like to possibly log messages from my app into ACTool if it's not too much work.
-DT |
how about if your script calls getattrib dtffxi timestamp, and dtffxi returns '0' or something else if there is something to timestamp? could be a workaround...
then your script timestamps when $DTtimestamp <> 0...
drewlt
Apr 19 2006, 06:35 PM
| QUOTE (cgrinds @ Apr 19 2006, 05:39 PM) |
how about if your script calls getattrib dtffxi timestamp, and dtffxi returns '0' or something else if there is something to timestamp? could be a workaround...
then your script timestamps when $DTtimestamp <> 0... |
I am wanting my program to generate an ACTool timestamp. This will not work.

-DT
Starhawk
Jun 21 2006, 01:54 AM
Ipa:
I wanted to get some info on the tGlobal struct, but I can't seem to find reference to it anywhere in the actool 5.3.0 source. For that matter, the referenced "memlocs.pas" could not be found anywhere in the source tree. Also, I have a couple questions about variables, and what their purpose is.
Any chance I can get you to run down this list for me? Some may be pretty obvious, but adding them for 100% clarity.
InstanceCount As Long
RunMacro(255) As Byte
ActCmdEvent(255) As Byte
ActCmdParam(255) As Byte
PromptAC_LoByte As Byte
PromptAC_HiByte As Byte
p1 As Byte
p2 As Byte
MacroStatus As Byte
ACToolHandle As Long
PluginVersion(255) As Byte
Ipa
Jun 21 2006, 06:15 PM
Hey there.
I'll give you the variable uses later this evening when I can get into the code, but just a heads-up that Cam is planning a 5.4 release soon. I fired off my 5.4 Alpha 8 code to him and one of the key changes was a complete revision to the tGlobal structure. The changes are:
- Remove a bunch of useless junk that were for Asheron's Call 1 (AC1) spell components
- Relocate variables that are 'generic' to the start of the structure, so that you don't need to pad over 10k bytes of AC1 specific stuff.
- Remove any variables that aren't referenced in ACTool code code
These changes mean that non-Asheron's call extensions (ie FFACT) that need to use the MMF, can focus on just the start of the structure and use a very small MMF size, ignoring all the trailing AC1 specific variables.
The new tGlobal definition as it will be in ver 5.4 is as follows. Everything after .PluginVersion is AC1 specific and can be ignored:
| CODE |
type TGlobal = packed record InstanceCount: Cardinal; Command: Integer; Param1: ShortString; Param2: ShortString; Param3: ShortString; Param4: ShortString; Param5: ShortString; Performed: ShortInt; Result: ShortString; Result2: ShortString; RunMacro: ShortString; ActCmdEvent: ShortString; ActCmdParam: ShortString; PromptAC: Integer; MacroStatus: Byte; ACToolHandle: DWord; PluginVersion: ShortString; _chatmessage: ShortString; _ChatMessageColor: Integer; _chatname: ShortString; _chattext: ShortString; _commandtext: ShortString; _craftchancesuccess: Double; _damageamount: LongInt; _damagelocation: LongInt; _damagesource: ShortString; _damagetype: LongInt; _equip: ShortString; _failure: Integer; _FellowCount: Integer; _FellowLeader: Integer; _FellowName: ShortString; _fellowrecruit: ShortString; _groupname: ShortString; _grouptext: ShortString; _houseopen: Integer; _idAcidProt: Double; _idArmorLevel: DWord; _idAttackBonus: Extended; _idBludgeonProt: Double; _idBurden: Integer; _idcharclass: ShortString; _idcharcoordination: DWord; _idcharcurhealth: DWord; _idcharcurMana: DWord; _idcharcurStamina: DWord; _idcharendurance: DWord; _idcharfellowship: ShortString; _idcharfocus: DWord; _idcharfollowers: DWord; _idchargender: DWord; _idcharleadership: DWord; _idcharlevel: DWord; _idcharloyalty: DWord; _idcharmaxhealth: DWord; _idcharmaxMana: DWord; _idcharmaxStamina: DWord; _idcharmonarch: ShortString; _idcharpatron: ShortString; _idcharPK: DWord; _idcharquickness: DWord; _idcharrace: DWord; _idcharrank: DWord; _idcharself: DWord; _idcharspecies: ShortString; _idcharstrength: DWord; _idColdProt: Double; _idDamage: Integer; _idDamageBonus: Extended; _idDamageRange: Double; _idDamageType: Integer; _idDyeable: Boolean; _idFireProt: Double; _idguid: integer; _idhealing: DWord; _idImbued: LongInt; _idImbuedBy: ShortString; _idInscribable: Wordbool; _idInscribedBy: ShortString; _idInscription: ShortString; _idLightningProt: Double; _idLockClose: Wordbool; _idLockOpen: Wordbool; _idlockpick: DWord; _idlorereq: DWord; _idMagicDefBonus: Extended; _idmanacost: DWord; _idmanamax: DWord; _idManaModifier: Double; _idManaRate: Double; _idmanaremaining: DWord; _idMaterialType: Integer; _idMeleeDefBonus: Extended; _idMissileDefBonus: Extended; _idPierceProt: Double; _idracereq: ShortString; _idrankreq: DWord; _idSkill: Integer; _idskilllevel: DWord; _idskillreq: DWord; _idskillreqid: DWord; _idSlashProt: Double; _idSpeed: Integer; _idspellcraft: DWord; _idspellids: ShortString; _idspelltypes: ShortString; _idTinkerCount: DWord; _idusescurrent: DWord; _idusesmax: DWord; _idvalue: DWord; _idwieldreq: DWord; _idwieldreqid: DWord; _idwieldreqtype: DWord; _idWorkmanship: Double; _idWorkmanshipItems: LongInt; _idWorkmanshipUnits: LongInt; _inflictdamageamount: LongInt; _inflictdamagesource: ShortString; _inflictdamagetype: LongInt; _inflictmeleeevade: Integer; _InsertInvDest: Integer; _InsertInvItem: Integer; _InsertInvKind: Integer; _InsertInvSlot: Integer; _killmsg: ShortString; _lastattacker: ShortString; _lastattackerguid: Integer; _LastDeath: Integer; _LoginComplete: Byte; _manaitem: ShortString; _manamana: Integer; _meleeevade: Integer; _monfollowers: Integer; _mononline: ShortString; _monsize: Integer; _MyHealth: DWord; _myid: LongWord; _mylevel: LongInt; _MyMana: DWord; _MyStamina: DWord; _portalstorm: Integer; _Pyreals: Integer; _servertext: ShortString; _spellhittarget: Integer; _spellready: Byte; _spellresist: Byte; _targethealth: LongInt; _targetname: ShortString; _targetoutofrange: Byte; _tellname: ShortString; _telltext: ShortString; _tradeaccept: ShortString; _tradeadd: ShortString; _tradeaddguid: Integer; _tradeclear: ShortString; _tradeend: ShortString; _tradeitemfail: LongInt; _trademoney: LongInt; _tradeperson: ShortString; _tradevalue: LongInt; _unequip: ShortString; _xpskill: Integer; _xpskillapplied: LongInt; _xptotal: DWord; _xpunassigned: LongInt; end;
|
Starhawk
Jun 21 2006, 11:57 PM
Ipa:
Can you please also add a generic command that can use 1-4 parameters?
RazorWing
Dec 19 2006, 02:18 PM
I found this to be slightly unclear in how i would use it in my VB and AC macro.
I designed a VB interface and i have my AC macro, now how do i make it all communicate with one another?
Ipa
Dec 19 2006, 04:39 PM
Can you elaborate on what you're trying to do ?
When you state "AC Macro" do you mean Asheron's Call macro, or ACTool macro for a different game ?
RazorWing
Dec 22 2006, 05:19 AM
AC macro for another game.
I started developing my own macro in Visual Basic designing my own commands, but i gave up as it was extremely difficult to create macros in VB. As i was use to creating macros in ACTool, i would love to be able to combine both VB and ACTool.
So if you can help, i would be very greatful.
Abder9
Feb 22 2007, 04:41 AM
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please
click here.