Trading Info With X-Plane

This is how you get data from X-Plane or send data to X-Plane to use for whatever purposes you like,
including running custom cockpits, motion platforms, or anything else you can think of.

Setting up the X-Plane network:

Hook up the computer that you are going to use to drive your special hardware or run your special software to the computer running X-Plane by a simple 100-base-t ethernet cable. If you are hooking up only a single machine directly to the X-Plane machine, then you have to use a CROSSOVER ethernet cable for the machines to talk to each other. If you are hooking up to a switch or router which can have plenty of machines all hooked up at once, then you should use a REGULAR ethernet cable to hook each machine to the switch. Windows users may need to buy ethernet cards for each machine. Macs come with ethernet support out of the box.

Now that you have the machines hooked together, go to the Internet Options screen in the Settings menu of the copy of X-Plane running on each computer. Enter the IP address and port of each machine that X-Plane will talk to. This will determine what IP address X-Plane sends each of it's data packets to. X-Plane sends data in the UDP format in little "packets". Each packet has the following form:

 

MESSx1_________________1

Where MESS is the ASCI message, which may be any of the options described below.

Then X-Plane will also package a miscellaneous-use "index" character, simply an 'x' or '0' for our purposes.

Then X-Plane will then send the ASCI character '1' on a Mac, '0' on a Windows machine.

X-Plane will then send the data ("_______"), wrapped up at the end with another '1' for Mac, '0' for Windows.

 

Getting data from X-Plane:

Go to the Data Output screen in the Settings menu. You can output all manner of data there! All the data you select for internet output will now go to the IP address and port you entered the internet screen over the ethernet cable. For each data selection that you make in the Data Output screen, X-Plane will send a little packet over the ethernet cable many times per second. Here is what the "packet" or "structure" looks like for each data selection:

 

DATA01_________________1

Where DATA indicates X-Plane is sending data from the Data Output screen.

The "_____" above is a data_struct, defined below:

struct data_struct // struct alignment is 4 bytes!
{
int index; // data index, the index into the list of variables you can output from the Data Output screen in X-Plane.
float data[8]; // the up to 8 numbers you see in the data output screen associated with that selection.. many outputs do not use all 8, though.
};

Now, if you have 10 data output selections, then there will be 10 of these structures sent in one big blob of data every few frames or so. NOW HERE IS AN INTERESTING TIP: YOU CAN ALSO SEND DATA PACKETS RIGHT BACK TO X-PLANE! So, for example, to enter radios or control deflections by UDP, simply send the DATA message described above to X-Plane by UDP, and X-Plane will use those messages for input and control of the sim! SEND A -999 FOR ANY VARIABLE IN THE SELECTION LIST THAT YOU JUST WANT TO LEAVE ALONE!

 

MOTI01_________________1


If you want to drive a motion platform with X-Plane, check that option in the internet setup screen and X-Plane will send this packet.

"______" is (in floating-point) pitch, heading, roll, sideways acceleration, vertical acceleration, and longitudinal acceleration.



Sending data to X-Plane:

Now that is how you get data out. To send it in, just SEND X-Plane a data packet exactly like the DATA one above. X-PLANE ALWAYS RECEIVES ON PORT 49000. You may send joystick deflections to fly the plane with your own hardware, or send in the X/Y/Z/the/psi/phi to actually place the aircraft to fly X-Plane (as a visual) with your own flight model. You could also take the X/Y/Z/the/psi/phi out of X-Plane using the data output above to use X-Plane as the flight model to drive your own visuals.

Now, say that you are writing an add-on or something for X-Plane and you want your motion-platorm or cockpit to send in a request to X-Plane to send a bunch of data out like this, because you are getting tired of going into the data output screen and making selections of data to output all the time. In that case you will SEND a packet just like the one above to X-Plane, but the label will be "DSEL".

 

DSEL01_________________1

Data selection, where the "______" will be a series of integers indicating which data output you want! (1 for the first in the list, 2 for the second, etc).

So "DSELx14561" would, on a Mac, request that X-Plane send the fourth, fifth, and sixth items in the data output screen many times per second to the IP address listed in the Internet Settings screen. DSELx1 is in characters, 4 5 6 are integers, and the final 1 is a character.


USEL01_________________1

Data UN-selection, where the "______" will be a series of integers indicating which data output you want stopped! (1 for the first in the list, 2 for the second, etc).

So "USELx14561" would, on a Mac, request that X-Plane send the fourth, fifth, and sixth items in the data output screen many times per second to the IP address listed in the Internet Settings screen. DSELx1 is in characters, 4 5 6 are integers, and the final 1 is a character. You would send in data to X-Plane to control it's position if you are using it as a visual and drving it from some other source, for example.


FAIL01_________________1

Fail a system, where the "______" will be an integer indicating which system to fail. The system to fail is sent as an integer number (not ASCI) from 0 to 256, where the 0 is the first failure listed in the failure window in X-Plane (currently the vacuum system) and incremented by 1 from there.


RECO01_________________1

Recover a system, where the "______" will be an integer indicating which system to recover. The system to recover is sent as an integer number (not ASCI) from 0 to 256, where the 0 is the first failure listed in the failure window in X-Plane (currently the vacuum system) and incremented by 1 from there.


NEED01_________________1

Also, you can send this UDP string to X-Plane to draw the custom needles and annunciators in the cockpit bitmap textures panel_test_linear.bmp and panel_test_nearest.bmp.

The NEED means you want X-Plane to draw a NEEDLE (or annunciator), The 0 is the unused index as usual, the 0 or 1 indicates if the signal came from a Windows or Macintosh machine. The "______" part is a "Needle" structure as follows:

struct need_struct // struct alignment is 4 bytes!
{
int type; // type of needle or annunciator
float x,y,a,s; // location, angle, and size
};

The type is 1 through 7 for the CUSTOM needles in the panel_test_linear.bmp, and 8 through 26 for annunciators from the panel_test_nearest.bmp. The x, y, a, and s are the x and y location on the screen in pixels, a is the angle of the needle (read like a compass, unused for annunciators) and the s is the size, in ratio to the cockpit bitmaps. Using this technique, you can draw any number of needles or annunciators on the X-Plane panel by sending in the request via UDP.

TEXT01_________________1

You can send this UDP string to X-Plane to draw any text you like! (Hint: Convert numbers to ASCI text to draw numbers). You can use this to draw any warnings, labels, indicators, or other numbers on the panel or elsewhere.

The TEXT means you want X-Plane to draw some TEXT, The 0 is the unused index as usual, the 0 or 1 indicates if the signal came from a Windows or Macintosh machine. The "______" part is a "Text" structure as follows:

struct text_struct // struct alignment is 4 bytes!
{
int font; // (100 through 117.. experiment as needed to choose your favorite)
float x,y; // (location on the panel)
char string[150]; // (the text you want to send
};


SNAP01_________________1

You can send these SNAPSHOT structures to control the various aircraft in X-Plane, either the one you are flying or the others buzzing around the sky. You could use this to control other planes for networked play, and things like that. The 'x' right after the SNAP should be the ASCI 0 (NULL, not character "0") to control your plane, 1 for first other aircraft, 2 for the second other aircraft, etc on up to the maximum number of planes (10 as of this writing). Here is the SNAP structure that goes in the "____" above:

struct snap_struct // SNAPSHOT FOR NET, REPLAY, MOVIES, ETC. USED FOR MULTIPLAYER OR 2-MACHINE LOCATION
{ // COMPRESSION HERE IS KEY FOR KEEPING DOWN MOVIE SIZES, RAM SINCE THIS STRUCT IS STORED THOUSANDS OF TIMES FOR REPLAY, LOAD TIMES FOR MOVIES, AND NET TRAFFIC FOR MULTI-MACHINE LAN LIKE FOR MOTION PLATFORM.
xflt net_time ; // MUST SEND time in cross-machine reference for carriers and frigates
xint latlonrefcode; // lat/lon ref for x/y/z to be based on!
xint int_sit ; // onground, smoking, running[8], burning[8], compressed into 1 var for for LAN traffic, movie size, and RAM of replay mode
xint con_defs1 ; // joy defs, flaps
xint con_defs2 ; // sweep, stab trim (needed for flying stabs), jettisonable load for bombing replay, nosewheel steering. (which depends on the wrong stuff to be auto-figured.. remember it is based on YK data not the FC data that is saved!)
xint con_defs3 [4]; // vectors and cyclics, needed for helos and vtols.
xint gear_sit [5]; // gear deployment and deflections, compressed into 1 var for for LAN traffic, movie size, and RAM of replay mode
xflt instrument[5]; // Vind, alt, VVI, turnrate, slip (instruments for the external view with 6-pack.. cannot add more per FD agreement)
xflt pln_loc [9]; // MUST SEND to move plane: x,y,z, vx,vy,vz, the,psi,phi (DO NOT USE LAT AND LON THE RES IS NOT THERE I TESTED IT! REMEMBER WE MAY PRINT 6 FIGURES OF OUTPUT, BUT THE LAST DIGIT CAN ONLY BE LIKE A 4 OR 8 OR 0 OR SOMETHING... THE RES IS NOT THERE AFTER CONVERSION MATH, ETC!)
xflt boat_xz [4]; // carrier/frigate x/z, DO NOT PUT IN THE STATE STRUCT OR IT IS NOT SAVED FOR THE REPLAYS AND MOVIES!
xint boat_psi ; // carrier/frigate psi, DO NOT PUT IN THE STATE STRUCT OR IT IS NOT SAVED FOR THE REPLAYS AND MOVIES!
xflt thro_omega[8]; // throttle for exhaust and smoke thickness and rpm for prop, compressed into 1 var for for LAN traffic, movie size, and RAM of replay mode
xint fail_dv [8]; // failure status, a full state not just 0/1, and dvinc for smoke puffs, and needed all the time for not having the engine smoking on the replay after the crash.
};

 

Some sample UDP source code if you do not know how to write UDP:

Now, this is all well and good, but not much use to you if you do not know how to write code to send and receive by UDP. In fact, you might be wishing that X-Plane could drive your stuff directly from a hardware card or something rather than sending UDP to another machine. We chose sending UDP to another machine because it is FAST. REAL FAST. We use async transfer wich sends the data to the other machine AT THE SAME TIME the computer is doing flight-model and other calculations, effectively taking ZERO time out to send the data. Since you can send the data from X-Plane with no frame-rate hit, you can load up the other computer with all the CPU-intensive stuff you want and it will not hurt X-Plane at all.

Now, you say you do not know how write code to send and receive data by UDP? No problem! Here is some source code from X-Plane that sends and receives data that you can use to quickly teach you how to write your own UDP code! This code include both Macintosh (APL) and Windows (IBM) source code. Like the rest of this manual, you may use, modify, and distribute it freely.

Stuff inside the #if APL is Macintosh code, stuff inside the #if IBM is Windows code, and other stuff is cross-platform.
If you are on a Windows machine, you can throw away the "#if APL" stuff.
If you are on a Macintosh, you can throw way the "#if IBM" stuff.

Some simple procs are not included and you must make your own. Like there is a proc called "alert" which gives an alert message...
you need to write your own such procedure to give errors however you want.

Also I use a proc called "C_to_C" to copy a C-string to another C-string...
you can handle this with your own procs I think.


#if APL
#define xflt float
#define xint int
#define xbol Boolean
#define xchr char
#define xadd unsigned char
#endif
#if IBM
#define xflt FLOAT
#define xint INT
#define xbol BOOL
#define xchr CHAR
#define xadd BYTE
#endif

enum
{
net_listen_error_buffer_too_large=2,
net_listen_error_rcv_failed =3,
net_listen_error_rcv_failed_from =4
};

struct inet_struct
{
xbol got_their_ip ; // all net init procs have been called and we have thei ip address for transfer
xint listen_error ; // last error... global for refence at an interup-safe location to display it
xint con_test_result ; // connection test result: 0=no connection, 1=getting info but other guy not hearing, 2=full connection

xchr str_ipad_us [strDIM] ; // IP we are at , in english
xchr str_ipad_them[strDIM] ; // IP we are sending to, in english
xchr str_mess_snd [strDIM] ; // mssage sent by user
xchr str_mess_rcv [strDIM] ; // mssage received by user
};

//????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????*/
//???READ DATA OVER INTERNET????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// This is an ASYNC call. It can get data at ANY TIME, even right in the middle of drawing a polygon or reading a mission file OR INTERPRETING THE DATA WE JUST READ!
// What happens if we get the mission data from the other machine right in the middle of a mission read? UNDEFINED BY ME THAT'S FOR SURE!
// We therefore copy all of the received data into buffers, which we will empty out in our update proc so data is never altered right in the middle of a procedure!
//
// Also we store the errors here and present them later since the listen routine below could get called at any time, again right in the middle of drawing something,
// so the regular alert routine could never work! Remember this proc is called at interupt time so regular graphics like macibm_alert will NOT WORK!
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
#if APL
TEndpoint* my_ep ; // our "endpoint" of data transfer
InetAddress their_address[5]; // THEIR ADDRESS IN FORMAT USED FOR SEND AND RECEIVE CALLS
#define data_event T_DATA // apple's name for the data-reception event
#endif
#if IBM
SOCKET my_socket ; // our "socket" for data transfer
ULONG their_address[5]; // THEIR ADDRESS IN FORMAT USED FOR SEND AND RECEIVE CALLS
#define data_event FD_READ // ibm's name for the data-reception event
#endif

const xint net_NUMB_buff=100; // UDP NUMBER OF BUFFERS for recieving async data... 10 per second for 10 seconds while we load missions or whatever while other guy yacks on and on.
const xint net_SIZE_buff=500; // UDP buffer SIZE for recieving async data... x-plane needs strNETDIM*2 for the aircraft name/path
xint data_size[net_NUMB_buff]; // SIZE of the buffer below that we actually use on this reception
xchr data_read[net_NUMB_buff] // data to read, global to hold in buffer for duration
[net_SIZE_buff]; // data to read, global to hold in buffer for duration
xchr data_send[net_SIZE_buff]; // buffer to SEND

#if APL
pascal void net_interrupt(void*,OTEventCode event,OTResult,void*)
#endif
#if IBM
void net_interrupt( LPARAM event )
#endif
{
if(event==data_event)
{
static xint buffer_filled=0; // work on the next buffer in the chain, realizing this proc could easily be called in the
buffer_filled=intwrap(buffer_filled+1,0,net_NUMB_buff-1); // middle of a parse of any other buffer! that should be ok since only buffers below this one in the array will be parsed.

#if APL
TUnitData unitdata;
unitdata.addr.maxlen =sizeof(InetAddress) ;
unitdata.opt.maxlen =0 ;
unitdata.opt.buf =0 ;
unitdata.udata.maxlen =net_SIZE_buff ;
unitdata.udata.buf =(UInt8*)data_read[buffer_filled] ;
unitdata.addr.buf =(UInt8*)&their_address[0] ;

xint done=Xfals; // opentransport needs to send data until it delivers a "kOTNoDataErr"...
while(!done){ // then and ONLY THEN am I primed for my next "T_DATA" event.
OTFlags flags=0;
OSStatus OSerr=(my_ep->RcvUData(&unitdata,&flags));
if(OSerr==kOTNoError )done=Xfals;
else if(OSerr==kOTNoDataErr)done=Xtrue;
else {done=Xtrue; if(DEV)net.listen_error=net_listen_error_rcv_failed;}}
data_size[buffer_filled]=unitdata.udata.len;
#endif
#if IBM
SOCKADDR_IN addr_wegotfrom; // UDP IS A CONNECTIONLESS PROTOCOL SO WE DO NOT CONNECT OR ACCEPT.. JUST SENDTO AND RECV!
int addr_len=sizeof(addr_wegotfrom);
data_size[buffer_filled]=recvfrom(my_socket,data_read[buffer_filled],sizeof(data_read[buffer_filled]),0,(LPSOCKADDR)&addr_wegotfrom,&addr_len);

// this checks where the message came from.. useful for server if IP add not sent in... if(addr_wegotfrom.sin_addr.s_addr!=their_address){...}
if(data_size[buffer_filled]==SOCKET_ERROR){if(DEV)net.listen_error=net_listen_error_rcv_failed; else buffer_filled-=1;} // ...see if we can continue????
#endif

if(data_size[buffer_filled]>=net_SIZE_buff)net.listen_error=net_listen_error_buffer_too_large;
}
}

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//???INTERPRET NET DATA IN A SAFE PLACE LIKE RIGHT HERE, NOT AT INTERUPT WHEN WE COULD BE IN THE MIDDLE OF WHO-KNOWS-WHAT???????????????????????????????????????????????????????
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// Remember that we could get an async listen while we are in the middle of this proc, thus adding another buffer! ZOINKS!
// This is not a problem since the async listener will tack a new buffer on above the one we are currently looking at.
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
void net_listen()
{
xint buffer,c;
for(buffer=0;buffer<net_NUMB_buff;buffer++)
if(data_read[buffer][0]!=0)
{
if(data_size[buffer]>=net_SIZE_buff)MACIBM_alert("Receiving too LARGE a buffer!","","","",t_exit);
xint len=data_size[buffer]-7; // there are 7 bytes of packaging... revaerse the packaging process now, checking for byte-order and getting the whole struct.
xint good_data=Xtrue; // assume we have good data unless we fail a check below.
if(data_read[buffer][5 ]!='0' && data_read[buffer][5 ]!='1')good_data=Xfals; // initial APL/IBM flag
if(data_read[buffer][6+len]!='0' && data_read[buffer][6+len]!='1')good_data=Xfals; // final APL/IBM flag, which also serves as a check to make sure the buffer was not stepped on before the send was finished.
if(data_read[buffer][5 ]!= data_read[buffer][6+len] )good_data=Xfals; // if we have different ideas about APL/IBM at end of struct as beginning, then struct was stepped on during transmission by typing a message, switching modes, etc.

if(!good_data)err<<"bad data:"<<data_read[buffer]<<"\n";
if( good_data)
{
xchr label[4]={data_read[buffer][0],data_read[buffer][1],data_read[buffer][2],data_read[buffer][3]};
xint index = char_to_int(data_read[buffer][4]);

xint diff_end=((data_read[buffer][5]!=int_to_char(APL)) ); // check to see if different endian, even if we do not reverse bytes here
xint rev_byte=((data_read[buffer][5]!=int_to_char(APL)) && // rev bytes if APL/IBM difference
(! strncmp(label,"XSYS",4)==0) && // do not rev XSYS (misc) messages... they are strings
(! strncmp(label,"MESS",4)==0) && // do not rev MESS (user) messages... they are strings
(! strncmp(label,"XUSR",4)==0) && // do not rev XUSR (virtual-world ID) messages... they are strings
(! strncmp(label,"FIRE",4)==0) && // do not rev FIRE (YMOD firing) messages... it is a string
(!(SIM && strncmp(label,"STAT",4)==0)) ); // do not reverse X-Plane STATE struct... it has strings

for(c=0;c<len;c++) // unpack the buffer, down-shifting it to a convenient place to work with it.
data_read[buffer][c]=data_read[buffer][c+6];

long add;
if(rev_byte) // reverse byte order if needed.
for(add =(long)&data_read[buffer][ 0]; // this byte-reverse-proc works as long as:
add< (long)&data_read[buffer][len]; // 1: the struct has no chars or doubles... only 4-bytes floats and ints
add+=4)rev_byte_order4((xadd*)add); // 2: the struct alignment is 4 on all machines

data_read[buffer][len]=0; // null-term the buffer in the downshifted form in case it is a string, as needed for the YMOD fir string, for example.


// now use the data_read[buffer] to do stuff. this is the recieved data
}

///??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// ZERO BUFFER SO NOT REUSED!
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
memset((xchr*)&data_read[buffer],0,sizeof(data_read[buffer]));
}
}

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//???SEND DATA OVER INTERNET????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// We are an ASYNC mode her for performance, and that even includes the damn SENDS!!!!!!!
// If I try to send a bit of data while a previous send is still in process, the first send will be over-written!
// Strange but Xtrue! So must never send too fast!
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
void net_send(xchr label[4],xint index,xchr* data_in,xint len,xint ip_index)
{
if(!net.got_their_ip[ip_index])MACIBM_alert("net_send failed since net not inited!","","","",t_exit);
if(len+10>=net_SIZE_buff )MACIBM_alert("Trying to send too LARGE a buffer!" ,"","","",t_exit);

data_send[0 ]=label[0]; // package the data with the label, and also a 0/1 for Apple/IBM.
data_send[1 ]=label[1]; // ALSO PUT ANOTHER APPLE/IBM AT THE END FOR A DOUBLE-CHECK TO MAKE SURE THE
data_send[2 ]=label[2]; // STRUCTURE WAS NOT STEPPED ON PARTWAY THROUGH THE SEND, AS CAN HAPPEN ON WINDOWS MACHINES!
data_send[3 ]=label[3]; // THIS IS BECAUSE WHILE MACS ONLY TAKE KEYBOARD EVENTS AT THE UPDATE PROC, WINDOWS TAKES
data_send[4 ]=int_to_char(index); // KEYBOARD EVENTS ASYNC, BRINGING IN USER-MESSAGES, MODE-CHANGES, OR WHO-KNOWS WHAT
data_send[5 ]=int_to_char( APL ); // AT ANY MOMENT, MAYBE STEPPING ON A STRUCTURE THAT IS BEING SENT OUT AT THE MOMENT!
data_send[6+len]=int_to_char( APL ); // (it happens with mode changes in youngs mod ALL THE TIME)
// WHEN THIS HAPPENS THE END OF THE STRUCT IS =>TRASHED<=.
memcpy(&data_send[6],data_in,len); // WE THEREFORE PUT AN APL/IBM AT THE END OF THE STRUCT AS WELL TO SEE THAT IT IS NOT TRASHED.
len+=7;

#if APL
TUnitData unitdata;
unitdata.addr.len =sizeof(struct InetAddress);
unitdata.addr.buf =(UInt8*)&their_address[ip_index];
unitdata.opt.len =0;
unitdata.opt.buf =0;
unitdata.udata.len=len;
unitdata.udata.buf=(UInt8*)data_send;
if(my_ep->SndUData(&unitdata)!=kOTNoError && DEV)MACIBM_alert("net_send failed!","","","",t_exit);
#endif
#if IBM
SOCKADDR_IN addr_otherguy; // UDP IS A CONNECTIONLESS PROTOCOL SO WE DO NOT CONNECT OR ACCEPT.. JUST SENDTO AND RECV!
memset(&addr_otherguy,0,sizeof(addr_otherguy)); // clear the address to send to first
addr_otherguy.sin_family =AF_INET; // ALWAYS use AF_INET for networking sockets.
addr_otherguy.sin_port =htons(str_to_int("49000")); // use the port we specified, probly 49000.
addr_otherguy.sin_addr.s_addr=their_address[ip_index]; // send to their IP address... UDP is a connectionless protocol so we have no connection... we must send to their IP.
if(sendto(my_socket,data_send,len,0,(LPSOCKADDR)&addr_otherguy,sizeof(struct sockaddr))!=len && DEV)MACIBM_alert("net_send failed!","","","",t_exit);
#endif
}

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//???INIT NET DATA??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
void net_init()
{
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// ZERO STRING AND SYSTEM INIT
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
memset((xchr*)&data_read,0,sizeof(data_read)); // zero out all receive buffers.
memset((xchr*)&data_send,0,sizeof(data_send)); // zero out all transmit buffers.
#if APL
if(InitOpenTransport()!=kOTNoError)MACIBM_alert("InitOpenTransport failed!","","","",t_exit);
#endif
#if IBM
WSADATA wsaData;
if(WSAStartup(MAKELONG(2,0),&wsaData)!=0)MACIBM_alert("Winsock init failed!","","","",t_exit);
#endif

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// GET OUR IP ADDRESS AND LOAD IT INTO THE STRING FOR DISPLAY
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
#if APL
SInt32 val=0;
InetInterfaceInfo us;
OTInetGetInterfaceInfo(&us,val);
OTInetHostToString(us.fAddress,net.str_ipad_us);
#endif
#if IBM
xchr my_name[strDIM]={0};
struct hostent *my_hostdat;
gethostname(my_name,strDIM); // get the name of this machine
if((my_hostdat=gethostbyname(my_name))==NULL)MACIBM_alert("Could not get our host!","","","",t_exit); // get this machine data with that name
C_to_C(inet_ntoa(*(struct in_addr*)my_hostdat->h_addr_list[0]),net.str_ipad_us); // copy machine IP address into global struct for printout
#endif

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// GET OUR ADDRESS AND BIND IT TO OUR LOCAL ENDPOINT OR SOCKET
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
#if APL
struct InetAddress my_address;
OTInitInetAddress(&my_address,str_to_int("49000"),0);

TBind req;
memset(&req,0,sizeof(TBind));
req.addr.len=sizeof(struct InetAddress);
req.addr.buf=(UInt8*)&my_address;

OSStatus OSerr;
TEndpointInfo info ;
my_ep=OTOpenEndpoint(OTCreateConfiguration(kUDPName),0,&info,&OSerr);

if(my_ep==NULL || OSerr!=kOTNoError)MACIBM_alert("OTOpenEndpoint failed!","","","",t_exit);
if(my_ep->InstallNotifier(net_interrupt,0)!=kOTNoError)MACIBM_alert("InstallNotifier failed!","","","",t_exit);
if(my_ep->SetAsynchronous( )!=kOTNoError)MACIBM_alert("SetAsynchronous failed!","","","",t_exit);
if(my_ep->Bind (&req,nil )!=kOTNoError)MACIBM_alert("Bind failed!","","","",t_exit);
#endif
#if IBM
SOCKADDR_IN my_address;
memset(&my_address,0,sizeof(my_address));
my_address.sin_family =AF_INET; // ALWAYS use AF_INET for networking sockets
my_address.sin_port =htons(str_to_int("49000")); // use the port we specified, probly 49000, htonl means Host to Network Short byte-order conversion
my_address.sin_addr.s_addr=htonl(INADDR_ANY); // INADDR_ANY means use OUR OWN IP address, htonl means Host to Network Long byte-order conversion

int reuse_address=1; // may be 0 or 1... third-party add-ons need this to be 1 to bind to the socket for their own needs.
if((my_socket=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP))==INVALID_SOCKET )MACIBM_alert("Can not create socket! ","","","",t_exit);
if(setsockopt(my_socket,SOL_SOCKET,SO_RCVBUF ,(const char*)&net_SIZE_buff,sizeof(net_SIZE_buff))!=0)MACIBM_alert("Cannot set socket buffer size!","","","",t_exit); // just to be neat and careful, set buffer size to the max size we will use
if(setsockopt(my_socket,SOL_SOCKET,SO_SNDBUF ,(const char*)&net_SIZE_buff,sizeof(net_SIZE_buff))!=0)MACIBM_alert("Cannot set socket buffer size!","","","",t_exit); // just to be neat and careful, set buffer size to the max size we will use
if(setsockopt(my_socket,SOL_SOCKET,SO_REUSEADDR,(const char*)&reuse_address,sizeof(reuse_address))!=0)MACIBM_alert("Cannot set socket buffer size!","","","",t_exit); // just to be neat and careful, set buffer size to the max size we will use
if(bind(my_socket,(LPSOCKADDR)&my_address,sizeof(my_address)) !=0)MACIBM_alert("Could not bind socket to port!","","","",t_exit);
if(WSAAsyncSelect(my_socket,mnw.wind,WM_USER,FD_READ) !=0)MACIBM_alert("Could not install notifier! ","","","",t_exit); // this automatically sets sockets to NON-blocking in windows! we tell Windows to notify our Window, using the WM_USER message, when of any of FD_ socket events we list is triggered.. right now we only have FD_READ, but we could | that with any number of args like this: FD_READ|FD_WRITE|FD_ACCEPT, etc.
#endif
}

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//???CLOSE NET DATA?????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
void net_close()
{
#if APL
my_ep->RemoveNotifier(); // cannot give errors above because the error-alert proc will try to close down before exiting,
my_ep->SetSynchronous(); // thus infinite-looping!
my_ep->Unbind ();
my_ep->Close ();
CloseOpenTransport ();
#endif
#if IBM
closesocket (my_socket); // cannot give errors above because the error-alert proc will try to close down before exiting,
WSACleanup ( ); // thus infinite-looping!
#endif
}

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//???REPORT TEST CONNECTION STATUS??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
void net_re_init_theirIP()
{
static xint inited=Xfals; // only call the full init once.
if(!inited)net_init(); // DO NOT CALL IT AS PART OF THE REGULAR INIT PROC OR THE MACHINE DIALS OUT EVERY TIME WE START X-PLANE!
inited=Xtrue; // only do our 1-time net init if needed.

//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
// FIND THEIR ADDRESS FOR LATER SEND AND RECEIVES AND SEE IF IT IS VALID AND NOT OURS
//??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
xint net_xworld=Xfals; // we use standard "login", or "connection check" by default.
#if SIM
net_xworld=set.net_xworld; // for x-plane, if requesting x-world, then switch to server login.
#endif

if(!net_xworld) // if not virtual earth...
{
xint customer_assign=0; // assign all customers their IP data right off... do not dally! we need this data right away!
static xint customer_send =0; // send customers one at a time to avoid stepping on each other. ORDER! I WILL HAVE ORDER!

for(customer_assign=0;customer_assign<5;customer_assign++)
if(net.use_their_ip[customer_assign])
{
#if APL
InetHost host_them;
net.got_their_ip[customer_assign]=(OTInetStringToHost(net.str_ipad_them[customer_assign],&host_them)==kOTNoError); // convert THEIR IP string into InetHost
OTInitInetAddress(&their_address[customer_assign],str_to_int("49000"),host_them); // convert THEIR InetHost into useable data
#endif
#if IBM
net.got_their_ip[customer_assign]=((their_address[customer_assign]=inet_addr(net.str_ipad_them[customer_assign]))!=INADDR_NONE);// convert THEIR IP string into useable data... inet_addr function return network-order so it is already HTONL'd.
#endif
if(strcmp(net.str_ipad_us,net.str_ipad_them[customer_assign])==0)net.got_their_ip[customer_assign]=Xfals; // see if it is our own that we entered and dis-allow it if so.
}

if(mnw.puff10) // 10 times a second bump up the customer to cycle thru all connections, reporting state to each of them
{
customer_send=intwrap(customer_send+1,0,4);

if(net.use_their_ip[customer_send])
if(net.got_their_ip[customer_send])
net_send("XSYS",0,((net.con_test_result>0)?str_broadcast_with_rep:str_broadcast_in_blind),15,customer_send); // do not use strlen for size arg since the string is not null-terminated!!! strlen will send too much data, slowing the machine or overflowing the receive buffer!
}
}







for windows users, add this bit to your event-handler to get "net_interrupt" whenever data comes in,
as flagged by the "WM_USER" message.


LRESULT CALLBACK IBM_handlewin(xwin wind,UINT uMsg,WPARAM wparam,LPARAM lparam)
{
switch(uMsg)
{
case WM_USER: // user message is any message defined by ME... I use it for callback of net data reception.
if(lparam==FD_READ)net_interrupt(FD_READ);
break;
...
...
...

for mac AND Windows users, call net_listen every cycle of the app if you goit the other guys IP, like this:

if(net.got_their_ip)net_listen()

(next section)