|
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()
|