Bridging the gap between MD and MCSI

The place to discuss scripting and game modifications for X³: Terran Conflict and X³: Albion Prelude.

Moderators: Moderators for English X Forum, Scripting / Modding Moderators

zanzal
Posts: 309
Joined: Sat, 15. Sep 12, 07:42
x3tc

Bridging the gap between MD and MCSI

Post by zanzal »

I thought I would do a quick write up having successfully bridged the gap between MD and MCSI. To keep this to the point, I'll assume you know enough about MCSI and MD and won't be explaining any of the commands used in either, but will give a brief description of certain problems and the code needed to overcome those problems.

Problem 1 - Letting MD know you want it to do something AKA calling into MD

This is the most straightforward problem to solve, by creating some ships in MD and hiding them somewhere in the Universe we can then register these ship objects and use the ship's name to call into MD from MCSI:

Code: Select all

<!-- Registering a ship object and letting MCSI know about it -->
<cue name="Lib_zanzal_init" delay="1s">
      <condition>
        <check_age value="{player.age}" min="5s" />  
      </condition>
      <action>
        <do_all>
          <create_ship name="Lib_zanzal_controlship1" race="xenon" racelogic="0" typename="SS_SH_X_M5" invincible="1" hidden="1" capturable="0" >
            <position x="900km" y="900km" z="901km"/>
            <sector x="0" y="0"/>
            <command command="stay" />
          </create_ship>
          ... same as above 9 times in my case
          <run_script script="lib.zanzal">
            <scriptargs>
              <scriptvalue datatype="integer" datavalue="0" />
              <scriptvalue datatype="array">
                <scriptarray>
                  <scriptvalue datatype="ship" datavalue="{Lib_zanzal_controlship1}" />
                  <scriptvalue datatype="ship" datavalue="{Lib_zanzal_controlship2}" />
                  ... Pass each ship
                </scriptarray>
              </scriptvalue> 
            </scriptargs>
          </run_script>
        </do_all>
      </action>
Now the script lib.zanzal gets called with a parameter 0 and a second parameter being the array of our ships.. In my case I reuse the same script file for MD calling MCSI and MCSI calling MD so I have an extra parameter that determins what command to run internally. 0 just means MD is calling MCSI to initialize the bridge.

Now we've got a way for MCSI to talk to MD because MCSI can store this array of ships in a global variable for later use. I cobbled this solution together several months back from various postings about MD.

Now changing the names of these ships in MCSI I can trigger an MD cue written like:

Code: Select all

<!-- Invoking the MD command to show a hint -->
       <cue name="Lib_zanzal_show_help" delay="1s">
          <condition>
            <check_value value="{object.name@Lib_zanzal_controlship1}" exact="show_help" />
          </condition>
          <action>
            <do_all>
              <show_help duration="{object.name@Lib_zanzal_controlship2}" force="1" text="{object.name@Lib_zanzal_controlship3}" />
              <run_script script="lib.zanzal">
                <scriptargs>
                  <scriptvalue datatype="integer" datavalue="1" comment="Command 1 - CMD completed (success or fail doesn't matter)"/>
                  <scriptvalue datatype="array" comment="Result: Dependant on the command that was called.">
                    <scriptarray>
                      <scriptvalue datatype="integer" datavalue="1" />
                      <scriptvalue datatype="string" datavalue="" />
                    </scriptarray>
                  </scriptvalue>
                </scriptargs>
              </run_script>
              <reset_cue cue="Lib_zanzal_show_help"/>
            </do_all>
          </action>
        </cue>
With just a few lines from MCSI code:

Code: Select all

$tip.text = $esc + 'OTIP:' + $esc + 'X' + ' Dont eat yellow snow.'
dim $args = 5000, $tip.text
= [THIS] -> call script 'lib.zanzal': command='show_help' args=$args
Image

I am now able to do something using MCSI code that can only be done from MD.
Last edited by zanzal on Sun, 9. Nov 14, 15:59, edited 4 times in total.
zanzal
Posts: 309
Joined: Sat, 15. Sep 12, 07:42
x3tc

Post by zanzal »

Of course there is another issue.

Problem 2. I only have one set of ships that I can manipulate and calling into MD I will need to yield in the calling MCSI script.

This was solved rather easily with some basic locking code. Given that MCSI is single threaded, I don't need anything more than a global variable that indicates that the subsystem is locked. If the variable is already set, then the caller has to wait until it is unset. This should be used with care then because we don't want this code to run on any of the player's assets as it could get interrupted. We also need to be careful not to run it on objects that could get destroyed or we risk locking the MD/MCSI bridge up for future calls if the script calling MD gets killed off.

Code: Select all

START $null -> call script 'lib.zanzal': command='show_help' args=$args
Starting the script as an independent task like above will help us avoid this problem. We can further address it by creating front end scripts that call lib.zanzal as a separate task and retrieve the return values by giving lib.zanzal an array to put them in. So maybe we make a lib.zanzal.showhelp that expands the arguments and handles some of this problematic stuff and then we get away from the issue entirely.

Problem 3. This all works well until we want to direct MD to do something to an existing in game object.

So we have a nice little bridge, but guess what? It turns out it is rather useless unless we can tell MD that we want to perform an operation against an object. So we need to be able to set an MD variable to an arbitrary object. So here is how we do that:

Code: Select all

set_object:
  $md.var = $args[0]
  $object = $args[1]
  if $object -> exists 
    $object.id = $object -> serialise object
    $object.id.sl = get length of string $object.id
    $object.id.sl = $object.id.sl - 3
    $object.id = get substring of $object.id offset=2 length=$object.id.sl
    if $object.id.sl > 0
      $arg1_obj-> set name to $md.var
      $arg2_obj-> set name to $object.id
      $cmd_obj-> set name to 'set_object'
      gosub wait_for_result:
    end
  end
endsub
Where $cmd_obj references MD variable Lib_zanzal_controlship1, $arg1_obj references Lib_zanzal_controlship2, etc. It turns out the command serialise object is good for something other than mail menus. We do have to trim off the useless garbage at the beginning and end of it, but then the string we have left over we can pass into MD.

Code: Select all

        <cue name="Lib_zanzal_set_object" delay="1s">
          <condition>
            <check_value value="{object.name@Lib_zanzal_controlship1}" exact="set_object" />
          </condition>
          <action>
            <do_all>
              <set_object name="{object.name@Lib_zanzal_controlship2}" value="{{object.name@Lib_zanzal_controlship3}}"/>
              <run_script script="lib.zanzal">
                <scriptargs>
                  <scriptvalue datatype="integer" datavalue="1" comment="Command 1 - CMD completed (success or fail doesn't matter)"/>
                  <scriptvalue datatype="array" comment="Result: Dependant on the command that was called.">
                    <scriptarray>
                      <scriptvalue datatype="integer" datavalue="1" />
                    </scriptarray>
                  </scriptvalue>
                </scriptargs>
              </run_script>
              <reset_cue cue="Lib_zanzal_set_object"/>
            </do_all>
          </action>
        </cue>
In case it looks confusing the code says when the name of our control ship is equal to 'set_object' then we set an MD variable named after our 2nd control ship to be the object referenced by the name of the 3rd control ship.. Pretty easy.

My MCSI code to call this function looks like this:

Code: Select all

dim $args = 'LZMD_PlayerShip', [PLAYERSHIP]
$result = [THIS]-> call script 'lib.zanzal' : command='set_object' args=$args
Really we don't want to use the playership since there is a better way to do that in MD, but you can use any object you want here and we're setting the MD variable 'LZMD_PlayerShip'.

That is pretty much it..

How you use this knowledge is up to you. In my case I am working on a library for my MCSI scripts that I can use to expand capabilities only available to MD scripts.
Last edited by zanzal on Sun, 9. Nov 14, 18:17, edited 1 time in total.
Joubarbe
Posts: 4796
Joined: Tue, 31. Oct 06, 12:11
xr

Post by Joubarbe »

Interesting, that should go to the tutorials section before falling into the deep void.
User avatar
X2-Illuminatus
Moderator (Deutsch)
Moderator (Deutsch)
Posts: 25130
Joined: Sun, 2. Apr 06, 16:38
x4

Post by X2-Illuminatus »

Joubarbe wrote:that should go to the tutorials section before falling into the deep void.
Added.

Also interesting with regards to the communication between MD and MSCI is h2o.Ava's [MD Tool] MD Control to be found in the Utility and Library Scripts section of the Community Mods & Scripts Download library.
Nun verfügbar! X3: Farnham's Legacy - Ein neues Kapitel für einen alten Favoriten

Die komplette X-Roman-Reihe jetzt als Kindle E-Books! (Farnhams Legende, Nopileos, X3: Yoshiko, X3: Hüter der Tore, X3: Wächter der Erde)

Neuauflage der fünf X-Romane als Taschenbuch

The official X-novels Farnham's Legend, Nopileos, X3: Yoshiko as Kindle e-books!
zanzal
Posts: 309
Joined: Sat, 15. Sep 12, 07:42
x3tc

Post by zanzal »

Thank you. I had encountered MDTool while I was researching the subject. The very last post in the MDTool thread was the inspiration for using just ships to perform the interaction.

I decided not to use MDTool as a starting point, because I felt the control scheme used was overly complex. As it turned out, everything can be done with just ships and ship names. You don't even need a dedicated sector.
User avatar
DrBullwinkle
Posts: 5715
Joined: Sat, 17. Dec 11, 01:44
x3tc

Post by DrBullwinkle »

Nice write-up, zanzal. :thumb_up:

If you choose to post your library when it is ready, then I am sure that people will find it useful.
zanzal
Posts: 309
Joined: Sat, 15. Sep 12, 07:42
x3tc

Post by zanzal »

Thanks DrBullwinkle. I won't be making an official library for others to consume. It just creates hassle for people when to install mod X you need mod Y and then if they distribute a version of it then it would create compatibility issues... I change something and break everyones mod but my own.. No, not gonna be seeing any of that from me. My rule is that it if it can't be installed using a single package from the plugin manager then it is too much hassle. :)

If anyone needs guidance with putting this into use or would like to see the code for a more complete example, I putting am my internal test files up on Github. Free public domain code, come n' git it.

https://github.com/Zanzal/LibZanzal

Return to “X³: Terran Conflict / Albion Prelude - Scripts and Modding”