Help - Search - Members - Calendar
Full Version: ACTools and VB using MMF
AC Tools Everything Macro > General Discussion > C++/Delphi/VB Development
drewlt
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. smile.gif

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
smile.gif -DT
Ipa
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
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
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
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.

smile.gif -DT

edit: Thanks for the attachment - that may answer my Q's
drewlt
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.

smile.gif -DT
DaMOB
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
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...
smile.gif -DT
Ipa
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
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. biggrin.gif
drewlt
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.

smile.gif -DT
cgrinds
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.

smile.gif -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
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.

smile.gif -DT
Starhawk
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
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
Ipa:

Can you please also add a generic command that can use 1-4 parameters?
RazorWing
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
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
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
Nicole Kidman Blowjob!
http://Nicole-Kidman-Blowjob.org/WindowsMe...hp?movie=245552
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Invision Power Board © 2001-2010 Invision Power Services, Inc.