Writing a latent function.

A refuge for those migrating from the fallen DXEditing.com and a place for general discussion relating to Deus Ex editing (coding, mapping, etc).
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Writing a latent function.

Post by atrey65789 »

Hi there, during the course of the last few months, I have been studying C++ on my own time and experimenting with importing different functions natively into the Unreal Engine. right now I am experimenting with connecting different USB devices to the Unreal Engine, especially midi devices. The thing is though, I have a function that I made, void GetMidiInput(), it waits for input from a midi device and prompts the input to the screen via BroadcastMessage ().. it works and all, but between detecting input, the entire game window freezes completely.

So, but I am asking for help with, is that how can I modify this function to where it can wait for input from the device without freezing the entire game? I know that's the main purpose of latent functions, but I can't find any documentation on how to wrote these types of functions for unrealed. any help would be appreciated. I will paste the functions code if I have to.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Writing a latent function.

Post by Hanfling »

I never tried writing latent functions before, so let me see if I get it right how they are implemented, and write the below a bit more generalized.

Basic idea behind latent functions if you can have code inside your state code (the thing with the labels in your states), which can block unrealscript state code execution. Samples of this are Sleep(), StrafeTo() or WaitForLanding(). Not entire sure about this, but state code execution seems to be only useable on Actors and is executed when the actor is ticked.

Latent functions may be declared with the latent keyword, but as far as I can tell this is just to give you an hint that it's a latent function and prevents you from calling them in non state code, otherwise the latent keyword seems to have no effect.

To implement an UnrealScript latent function you basically need to native functions, one I will call entry function and a polling functions. The entry function is what you will call in UnrealScript, the second one is typically not declared in UnrealScript to prevent direct uc calls to it. The entry function will set StateFrame->LatentAction to the number of the polling native. During ticking if StateFrame->LatentAction is non zero this will be executed instead of continuing the normal state code flow. DeltaTime will be passed in inside the Result variable of the polling native. So basically at some point you need to set StateFrame->LatentAction to zero so normal code execution in UnrealScript continues, e.g. your latent functions "returns". Both of these native function need to be non blocking.

Actor also has some predeclared variables intended to be used with latent functions, for example to implement a timeout.

Code: Select all

var const byte            LatentByte;    // Internal latent function use.
var const int             LatentInt;     // Internal latent function use.
var const float           LatentFloat;   // Internal latent function use.
var const actor           LatentActor;   // Internal latent function use.
Now some example how this should work:

Code: Select all

// UC:
class LatentSampleActor extends Actors
  native;

native(3524) final latent function LatentSample();

event Receive( string Data );
event Timeout();

auto state Stuff
{

Begin:
  LatentSample();
}

// C++
#define LATENTSAMPLE_POLL_ID 3525

// Entry function.
void ALatentSampleActor::execLatentSample( FFrame& Stack, RESULT_DECL )
{
  guard(ALatentSampleActor::execLatentSample);
  P_FINISH;

  GetStateFrame->LatentAction() = LATENTSAMPLE_POLL_ID;
  LatentFloat = 0.f;
  unguardexec;
}
IMPLEMENT_NATIVE(ALatentSampleActor,3524,execLatentSample);

// Polling function.
void ALatentSampleActor::execLatentSamplePoll( FFrame& Stack, RESULT_DECL )
{
  guard(ALatentSampleActor::execLatentSamplePoll);
  // No P_FINISH as this isn't called by uc and hence can't serve it's purpose to eat up the EX_EndFunctionParms code.

  // Grab DeltaTime.
  FLOAT DeltaTime = *(FLOAT*)Result;

  // Increase timer used for timeout detection.
  LatentFloat += DeltaTime;

  // Fire event if data is ready
  if ( HAS_DATA() )
  {
    eventReceive( GET_DATA() );
    LatentFloat = 0.f;
  }
  // Timeout.
  else if ( LatentFloat>30.f )
  {
    eventTimeout();

    // Make "LatentSample function return" as viewed from UnrealScript.
    GetStateFrame->LatentAction() = 0;
  }
  unguardexec;
}
IMPLEMENT_NATIVE(ALatentSampleActor,LATENTSAMPLE_POLL_ID,execLatentSamplePoll);
So basically for your problem you will still have to either move your blocking i/o calls to another thread or use non blocking i/o calls for reading the input in your code.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

Very interesting. That explains why what I'm doing currently prevents the rest of the engine's code from executing. Very informative, Hanfling. Thank you. I will try this method today and let you know how it goes.
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

I seem to be getting some errors here.

error C2227: left of '->LatentAction' must point to class/struct/union

It shows this error two times, so, I don't think it's finding anything for 'GetStateFrame'.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Writing a latent function.

Post by Hanfling »

Missed the () after GetStateFrame() which is a function.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

Neat, fixed that. Also, "LatentAction()" was supposed to be "LatentAction". Gonna fix the other simple errors on my part and see if it works.
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

error C2059: syntax error : 'constant'

I got this on the IMPLEMENT_NATIVE definitions. Could I use AUTOGENERATE_FUNCTION instead since it has the exact same parameters?
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

Bump. Anybody know that error? Still messing with it and can't get it to work.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Writing a latent function.

Post by Hanfling »

Post project you want to compile.

You can use the AUTOGENERATE_FUNCTION macro to avoid having to manually put your IMPLEMENT_FUNCTION into cpp files, see NativeSamplePackage in ut/rune pubsrc for example use.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

This is the header

Code: Select all

/*===========================================================================
    C++ class definitions exported from UnrealScript.
    This is automatically generated by the tools.
    DO NOT modify this manually! Edit the corresponding .uc files instead!
===========================================================================*/
#if _MSC_VER
#pragma pack (push,4)
#endif

#ifndef UNREALMIDI_API
#define UNREALMIDI_API DLL_IMPORT
#endif

#ifndef NAMES_ONLY
#define AUTOGENERATE_NAME(name) extern UNREALMIDI_API FName UNREALMIDI_##name;
#define AUTOGENERATE_FUNCTION(cls,idx,name)
#endif

AUTOGENERATE_NAME(MidiMessage)

#ifndef NAMES_ONLY


struct AUnrealMIDI_eventMidiMessage_Parms
{
    FString midi_msg;
    FString midi_velocity;
};
class UNREALMIDI_API AUnrealMIDI : public AActor
{
public:
    DECLARE_FUNCTION(execGetMidiInput);
    DECLARE_FUNCTION(execMidiPoll);

    void eventMidiMessage(const FString& midi_msg, const FString& midi_velocity)
    {
        AUnrealMIDI_eventMidiMessage_Parms Parms;
        Parms.midi_msg=midi_msg;
        Parms.midi_velocity=midi_velocity;
        ProcessEvent(FindFunctionChecked(UNREALMIDI_MidiMessage),&Parms);
    }
    DECLARE_CLASS(AUnrealMIDI,AActor,0)
    NO_DEFAULT_CONSTRUCTOR(AUnrealMIDI)
};

#endif

AUTOGENERATE_FUNCTION(AUnrealMIDI,3524,execGetMidiInput);
AUTOGENERATE_FUNCTION(AUnrealMIDI,3525,execMidiPoll);

#ifndef NAMES_ONLY
#undef AUTOGENERATE_NAME
#undef AUTOGENERATE_FUNCTION
#endif NAMES_ONLY

#if _MSC_VER
#pragma pack (pop)
#endif

This is the source
// ===========================================================================
// C++ Class to implement full MIDI-Input Support for the UnrealEd (V 1.0).. cool, eh?
//
// By: Brenden Dane, Dane Audio Productions
//
// ===========================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#include "UnrealMIDIPrivate.h"

FString Msg;
FString midi_velocity;
bool bDone;


struct midi_in_list_node
{
int midi_in_number;
HMIDIIN midi_in;
struct midi_in_list_node *next_midi_in_list_node;
};

midi_in_list_node *opennode; //Current node for the current MIDI device.
HMIDIIN opendevice;

struct midi_in_list_node *first_midi_in_list_node = NULL;

#define LATENTSAMPLE_POLL_ID 3525

#define NAMES_ONLY
#define AUTOGENERATE_NAME(name) UNREALMIDI_API FName UNREALMIDI_##name;
#define AUTOGENERATE_FUNCTION(cls,idx,name) IMPLEMENT_FUNCTION(cls,idx,name)
#include "UnrealMIDIClasses.h"
#undef AUTOGENERATE_FUNCTION
#undef AUTOGENERATE_NAME
#undef NAMES_ONLY
void RegisterNames()
{
static INT Registered=0;
if(!Registered++)
{
#define NAMES_ONLY
#define AUTOGENERATE_NAME(name) extern UNREALMIDI_API FName UNREALMIDI_##name; UNREALMIDI_##name=FName(TEXT(#name),FNAME_Intrinsic);
#define AUTOGENERATE_FUNCTION(cls,idx,name)
#include "UnrealMIDIClasses.h"
#undef DECLARE_NAME
#undef NAMES_ONLY
}
}



IMPLEMENT_PACKAGE(UnrealMIDI);

void CALLBACK midi_in_handler(HMIDIIN midi_in, UINT msg_type, DWORD user_data, DWORD midi_msg, DWORD param2)
{
UINT midi_in_number;

midiInGetID(midi_in, &midi_in_number);

if (msg_type == MIM_DATA)
{
union
{
DWORD dwData;
BYTE bData[4];
}
u;

u.dwData = midi_msg;


switch (u.bData[0] & 0xF0)
{
case 0x80:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0x90:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xA0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xB0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xC0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xD0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
case 0xE0:
{
Msg = FString::Printf(TEXT("%02X"), u.bData[1]);
midi_velocity = FString::Printf(TEXT("%02X"), u.bData[2]);
bDone = true;
midiInStop(midi_in);
midiInClose(midi_in);
break;
}
}
}
}

void AUnrealMIDI::execGetMidiInput( FFrame& Stack, RESULT_DECL )
{
guard(AUnrealMIDI::execGetMidiInput);

P_GET_INT(port);
P_FINISH;

int midi_in_number = port;

GetStateFrame()->LatentAction = LATENTSAMPLE_POLL_ID;
LatentFloat = 0.f;

HMIDIIN hMidiDevice = NULL;
MMRESULT rv;

rv = midiInOpen(&hMidiDevice, midi_in_number, (DWORD)(void*)midi_in_handler, NULL,CALLBACK_FUNCTION);

midiInStart(hMidiDevice);

hMidiDevice = NULL;

unguardexec;
}

IMPLEMENT_NATIVE(AUnrealMIDI,3524,execGetMidiInput);


void AUnrealMIDI::execMidiPoll( FFrame& Stack, RESULT_DECL )
{

guard(AUnrealMIDI::execMidiPoll);

FString M;

// Grab DeltaTime.
FLOAT DeltaTime = *(FLOAT*)Result;

M = FString::Printf(TEXT("Poll called"));
eventBroadcastMessage(M,0,NAME_None);

// Increase timer used for timeout detection.
LatentFloat += DeltaTime;

// Fire event if data is ready
if (bDone)
{
LatentFloat = 0.f;
eventMidiMessage(Msg,midi_velocity);
GetStateFrame()->LatentAction = 0;
bDone=false;
}

unguardexec;
}

//IMPLEMENT_NATIVE(AUnrealMIDI,LATENTSAMPLE_POLL_ID,execMidiPoll);


IMPLEMENT_CLASS(AUnrealMIDI);
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Writing a latent function.

Post by Hanfling »

Mh IMPLEMENT_NATIVE should have been IMPLEMENT_FUNCTION in the first place, in any case in your code you won't need those as you do this with the AUTOGENERATE_FUNCTION stuff with it been declared inside the header.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

Hanfling wrote:Mh IMPLEMENT_NATIVE should have been IMPLEMENT_FUNCTION in the first place, in any case in your code you won't need those as you do this with the AUTOGENERATE_FUNCTION stuff with it been declared inside the header.

Alrighty alright. I did the AUTOGENERATE_FUNCTION way as you stated, It compiles and such, but it seems that there's no waiting for input before the function finishes its execution. (I have it looping with a 0.1 second sleep so I can see if it actually pauses until there is input)
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

Bump, code still compiles.. but it seems that the Poll function (which I assume is supposed to loop during the latent function) is not being called whatsoever.
Hanfling
MJ12
Posts: 406
Joined: Sun Oct 04, 2009 6:54 pm

Re: Writing a latent function.

Post by Hanfling »

You should post the uc part, and keep indention. It's horrible to read without. Also project file + download as a zip, so it's ready to build would be helpful.

Also, is bDone ever properly intialized? It should probably also be used a class variable rather than a global one. And you don't even get a first poll function call? If thats not the case you might just very early have your callback setting it to bDone.

Also, you never call RegisterNames(). You can probably just throw this into the actors ctor.
I demand my DXE User ID back. Launcher for DeusEx, Rune, Nerf, Unreal & Botpack. HX on Mod DB. Revision on Steam.
atrey65789
Thug
Posts: 49
Joined: Tue Jun 26, 2012 3:32 am

Re: Writing a latent function.

Post by atrey65789 »

https://drive.google.com/file/d/0B_YWF0 ... sp=sharing

There's a link to the whole source, UC, private header, etc. Should be ready to compile except maybe the path of the LIBs and Headers.


Do I call RegisterNames() in the native functions I am importing?
Post Reply