The online racing simulator
C++ - CInsim - A basic C++ InSim library (for Windows & UNIX/Linux)
Quote :Changelog:

UPDATE:
I haven't updated the library or been keeping track of InSim changes since January 2013. Forum member denis-takumi has created a repository on github.com that might be more stable and up to date than the files in this post. Check it at: https://github.com/turbosnail/cinsim

I have been off from LFS for a long time now and I'm not sure if I'll be back, but I receive email notifications of private messages and replys in this thread. I'll drop by to update whatever I can in the first post when I have time.

0.7 (Thanks to MadCatX for major improvements in this version)
  • Supports all new InSim changes introduced as of LFS 0.6C.
  • Updated send_packet(). It now properly handles both IS_BTN and IS_MTC packets and sends only the amount of data that is needed.
  • Removed send_button() as it is no longer needed (this breaks compatibility with old apps).
  • Simplified next_packet() and udp_next_packet().
  • Got rid of 100% CPU load caused by next_packet() on Linux systems by replacing select() for pselect() (other *NIX systems that use their own standard C library might exhibit a different behavior).
0.6
  • Added support for *NIX platforms. You only have to change in the insim.h file the "#define CIS_WINDOWS" preprocessor directive to CIS_LINUX, and you are ready to compile the lib under UNIX/Linux with the exact same functionality. (Portability code provided by MadCatX)
  • The additional function mstrostr() only uses ANSI-C functions now to ensure platform portability. All the occurrences of itoa() have been replaced by sprintf().
0.51
  • Fix for the send_button() method. Now it's thread-safe, there was a small typo in one line.
  • The additional function mstrostr() has been renamed to ms2str() and now has an optional third parameter to indicate if the resulting string will contain hundredths of second (default) or thousandths of second.
0.5
  • New method called send_button(), used to send BTN packets of variable size. Up until now button packets were always 240 chars long in the text field, thus producing a big network overhead when a lot of buttons were sent. Now you have to create a button struct and fill it to your desire. The send_button method calculates the text length and only sends the appropiate amount of data. It also refills the 'Size' field of the struct based on the text length so you don't have to worry about that.
  • Fix for the thread-safe send_packet() method. Actually, it was supposed to have been updated in the V0.4 but it never was because I didn't add a couple of lines... Now it's suppossed to be working fine.
0.4
  • Now uses pthreads-w32 for thread-safe send_packet() method (several threads can use safely the send_packet() without overlapping). Before it had to be controlled by the application programmer.
0.31
  • Adds all changes done to insim as of LFS S2 0.5Z28
0.3
  • First stable version.

I started using InSim with TheAngryAngel C tutorial and built my own C++ library to use InSim. This library is now functional and stable. The library is called CInsim and it's meant for writing simple C++ InSim applications. It was originally written for Windows but UNIX/Linux portability was added in v0.6.

Most of the work must be done "manually". Creating and sending packets, multi-threading when needed and other stuff is responsability of the programmer. This library uses InSim as-is, and reading the insim documentation is a must when you have to manage packets.

What the library offers you is the basics of communicating with InSim:
  • Establishing the connection to InSim (full TCP, full UDP, TCP+UDP), and using all possible parameters accepted by InSim IS_ISI (initialization) packets.
  • Keeping the connection always alive.
  • Keeping the packets coming one at a time.
  • Peek the type of the next packet, so you know if you have to process it or skip it.
  • Return the contents of the next packet so you can process it (in case it was one of the types you wanted).
  • Send packets via the main connection (thread safe since v0.5).
  • Send size trimmed button packets via the main connection (thread safe since v0.51)
  • Close connection to InSim.
  • There's also a function to convert miliseconds to a C string (this is an independent function).
You must cast the contents of the packets to a certain InSim packet struct to access the fileds. This is shown in the working examples linked below.


EXAMPLES:

Note1: Some of these examples don't use the latest version of CInsim, so if you are going to work on your own project, be sure to grab the latest version in this thread.

Note2: These examples might use functions that aren't ANSI-C defined, so maybe you won't be able to compile them right away under UNIX/Linux, sorry.

I have posted three examples using this library, so I'll link to them instead of uploading the same files again:

CInSim V0.7:
  • Event Control: (Note: This was previously released using CInsim 0.6, but has since been updated to 0.7) This application is an event manager (it's server oriented). It was originally designed to enforce HARDCORE qualify sessions, in which the use of Shift+P and Shift+S is forbidden, so people can only do realistic pitstops. The current version allows the use of Race Directors, appointed by adding their lfs usernames in a text file, who can do more stuff to manage an event (restarting, ending, auto-reversing top grid drivers, director chat messages, etc.). http://www.lfsforum.net/showthread.php?p=1403034
CInSim V0.3:
  • LFS Session Timing: This application shows sector times and time differences to the session best when you cross a checkpoint. It uses buttons, independent threads for button timers and some interesting stuff. http://www.lfsforum.net/showthread.php?t=46643
  • X-Y-Z Positioning: This application shows the coordinates of your car and the name of the closest player to your position. Of course, more than one car must be online for a name to show up! It uses TCP for main connection and UDP for MCI packets.http://www.lfsforum.net/showthread.php?t=47675
The examples use the CInsim library (of course) and already come zipped with all the required files to compile and even an executable if you can't compile them. Also, code::blocks project files are provided for each example.
Attached files
CInSim v0.7.zip - 66.3 KB - 1581 views
I was thinking of learning C++, so in future this might be handy for me .
#3 - sun
Thank you!, i've been waiting for one of these to come out for along time

I remember someone giving me somthing like this, but it might of been something else.. Anyways. Thanks!

Owen.
Good luck sun with these programming language

Hope you get it
What's required to get this to compile, on both Windows and Linux?

Quote from MaKaKaZo :RESTRICTIONS:

The insim.h file has been modified from the original InSim.txt provided in the doc folder. Theoretically, you can have buttons with variable length text up to 240 characters, but I had to fix this to a constant value, which I chose to be 112. I don't know how to make this library work with a variable value in that field. If anyone knows please tell me how to do it!

Anyone know how to fix this limitation?
I don't know why there is any sort of limitation for the button text, never ran into that. Although I didn't use this library I just made my own and it seems to work find sending that variable lengthed packet. If he had to choose a constant value why not choose the maximum?

About windows vs linux;

closesocket(s); in windows becomes close(s); in linux/unix

windows needs WSAStartup() and WSACleanup() calls at the start and end of the networking project where as linux would not.

Using blocking sockets would require a call to fcntl() instead of ioctlsocket(). There are probable a few other things I am noth thinking of. I have never written networking for linux/unix or other operating systems however I have read and remembered those bits of info - somehow...
Quote from Dygear :Anyone know how to fix this limitation?

It's the length of the text to the nearest multiple of 4.
TEXT_SIZE = ceil(TEXT_LEN / 4.0) * 4;

Quote from DarkTimes :It's the length of the text to the nearest multiple of 4.
TEXT_SIZE = ceil(TEXT_LEN / 4.0) * 4;


So, they are 'packed strings' then, but still use full int's when they are crossing the Ethernet buffer?
Quote from Dygear :So, they are 'packed strings' then, but still use full int's when they are crossing the Ethernet buffer?

Network traffic is not typed, if anything it's split into octets. One character corresponds to eight octets, or one byte (eight bits), this is always the case even when multi-byte character encodings are used because LFS first converts them to its own "encoding".
The reason it has to be a multiple of 4 is because LFS uses the packet size for validation purposes, at least that's the explanation I came up with.

Long story short: Fill your string with null bytes until its length is a multiple of 4

Edit: And yes, this is important because LFS interprets an additional 0 followed by text (or random data, which is what you would send if the memory is not Zero'd) as Caption/Text separator!
Quote from DarkTimes :
TEXT_SIZE = ceil(TEXT_LEN / 4.0) * 4;


The limitation as I understand it (I'm OK with C, but not fluent by any means) is that, in the header, you have to define a constant/fixed size for the text string when defining the struct.

What would be the best (or at least a good) way of implementing a variable length struct/packet/whatever?
Should the text field be defined in insim.h at all or is there a way of trimming the size of the struct/packet later when it's sent or is there a better way than either?
I believe LFS doesn't use a "variable sized struct", the struct simply has a pointer to the button text and the TextSize is used to determine the necessary amount of memory required to hold the string. However, there is no reason why this has to be a multiple of 4 by what information we have, we simply have to assume that this is required by LFS for whatever it does with buttons.

Simple illustration:
  • LFS receives button (identified by the Type)
  • compare received bytes to packet's Size, wait for more data if it's fragmented TCP data and the size and received byte count don't match, discard otherwise for being invalid.
  • If valid, create struct for the button, allocate memory for the text (of length TextSize)
  • memcpy the socket buffer contents starting at the offset for text to start+len
  • Set text pointer in struct to newly allocated and filled memory.
Quote from Degats :The limitation as I understand it (I'm OK with C, but not fluent by any means) is that, in the header, you have to define a constant/fixed size for the text string when defining the struct.

What would be the best (or at least a good) way of implementing a variable length struct/packet/whatever?
Should the text field be defined in insim.h at all or is there a way of trimming the size of the struct/packet later when it's sent or is there a better way than either?

I dunno, I've never really used InSim with C, but I imagine you could just set the Text size to 240 in the struct and then set the correct size of the packet when you send it.

IS_BTN btn;
strcpy(btn.Text, TEXT);
btn.Size = 12 + (ceil(TEXT_LEN / 4.0) * 4);
send(socket, (char*)&btn, btn.Size, 0);

That codes untested, just to demonstrate what I mean.
Yeah, pretty sure structs have to be a static size. Which means in InSim for the buttons you'd have to define TEXT_SIZE yourself between 0-240 depending on what your program does. Smaller the better of course.. Once defined, the struct works as usual. No problems I can see.

But for a variable length struct? Well, that's an array or in C++ a std::vector or a special class (something that returns an array or vector that's the packet) would be better. Just get your text length, fill in the data and ship it Seems like it would work..
The multiple of 4 is stated in insim.h and is presumably either because of some networking limitation or just that 4 bytes conveniently fits in 32bits of memory.

I had forgotten that in the original insim.h, the text portion of the button packets is commented out. Presumably then, one way to create the packet would be to extend the struct or append the text somehow before sending? (calloc()/malloc()/realloc() ? )

ed: DarkTimes and Stuff posted while I was writing the above - I'll have a play in a day or two when I get the time
Quote from Degats :(calloc()/malloc()/realloc() ? )

This is why I find C, and C++ much to difficult for new programmers and why I'm trying to make a simple interface for writing powerful InSim applications. I can't really think of anything you won't be able to do with PAWN that you can't do with C, and C++ once people start using it.

I'm trying to make C application based off this code that will allow me to write PAWN plugins for the InSim system. It C application will be the 'glue' between the plugins and the LFS TCP/UDP/IP interface. I think this would be fantastically powerful once implemented, and easy for new people to get into programming. PAWN has a proven track record with the likes of AMX Mod X as being easy to program for, and fast enough to execute within each frame of a FPS.
You don't need to allocate any memory to pull this off. Simply have it defined as text[240] inside the Insim packet; however when sending it get the size of your string and pad it until it is 4 bytes. The struct IS FIXED size, but what you SEND to LFS is variable sized; the reason for this is likely to save on bandwidth because a lot of InSim applications have lots of buttons going back and forth.
First of all, sorry for leaving this thread totally unassisted. I haven't been programming anything at all in the last year. Totally by chance I ended here and I find a discussion going on in the last days.

The thing about defining the text size in buttons struct is what has already been said. That line is commented in the original insim.txt file and there's an explanation that it can have a variable size up to 240 bytes, thus I didn't understand how a struct could have a member with a variable size. So, I chose 112 in my lib, but it can be changed of course to 240. I always send packets of the same size, or at least that's what I think

I'm not a good programmer so I didn't understand very well how to overcome this so I could send packets with variable text lengths, saving bandwith when the text is short.

What blackbird says makes sense. I don't know right now what changes should be done to the code to allow this. I don't think I'll have time nor feel like having a look at it anytime soon. Also I think that there have been newer updates to insim which I'm not sure if they affect this library.
Regardless thanks for the code, MaKaKaZo, it has been a great help.
Qual Control has now been updated and changed to Event Control.

Also, there's a new version 0.31 of CInsim, which includes all changes made to insim as of LFS S2 0.5Z28. The previous version v0.3 was done in 2008, so it had to be updated
I have updated the CInSim library to a new version v0.4. This new version now uses pthreads-w32 to achieve thread-safe sending. Previously the programmer neede to use some kind of mutual exclusion checking so several threads didn't use the send_packet( method at the same time. Now the library does this checking.


Also, Event Control has been updated again with new features and it's the first CInSim application to use the version 0.4 of the library.
#22 - PoVo
Hey! I was wondering, if maybe anyone could make an example of how you could use this, to make a button in a server displaying the time and date? Thanks.
In case you just want to show the date and time for a brief moment then you just create a button and use the appropiate function to retrieve date and time.

If you want it to be updated so it's continuously showing the time, then you have to create a separate thread to act as a timer. That thread, in a loop, updates the text in the button and then sleeps for 30 or 60 seconds (or less, in case you want to update by minutes, depending on the acuracy you want), or 1 second (if you want to update each second).

The thread repeats that in a loop until it receives some kind of signal to stop and finish its execution (to avoid an endless loop). I guess using a global variable would be the way I would do it, but probably the pthreads library adds some kind of mechanism to communicate threads. You can use the pthreads library that I have been using in my examples. You can grab the "Event Control" example application (first post) and have a look at the code. I use threads there for timers, to erase buttons after a certain amount of time.

Anyway, I'm not a good programmer and I'm not thinking this very thoroughly...
#24 - PoVo
Yes, i know the timer bit, i just don't know where to start, e.g How to make the button itself? In C# I get predictive functions when i type something, but in C++ i don't
Look in the header files, do some searching. I mean, sure there are tools out there for the auto typing "lazy" behavior. This is my opinion, though I don't look down on people using this stuff, I do disguise it because it makes too many people rely on the feature. Point proven here. Just start with the main .h files, and see what gets included.

Good luck, and I can't really help with the project specific stuff because I don't use it. So sorry I can't give a direct header file to look at, but you must be including it somewhere, so start there.

EDIT: Forgot to mention, the first place to look (which I hope you have), IF AVAILABLE, is some form of ReadMe, or other documentation that may come with the library, or code. Again I don't know the specifics of this library.

FGED GREDG RDFGDR GSFDG