You only need to bind packets when your program first starts, you just bind them once and then forget about them. You should not be binding packets from the callbacks of other packets. That will cause huge problems. And it is not needed.
Anyway, the source code for Spark is available from the first post, you can look at the IPacketBinding, PacketBinding and BindingCollection classes to see how the packet binding is handled. It's very, very simple. It's just a map of function pointers. You register which packets you want to be notified about, then when that packet is received Spark loops through them and calls each function passing the packet object as a parameter.
Yes, you can bind callbacks at any point, and you can bind multiple callbacks to the same packet. You cannot currently unbind callbacks, as I haven't written that yet. However binding callbacks from within another callback looks like a really bad idea to me, I can't think of a reason why you would want to do this.
Edit: In fact I can see a million reasons how doing this would completely **** up your program.
The way I've done it in the past if just to create a simple console app that writes all the socket receive data from LFS to a file. It's all byte data so you can just dump it straight out. You can load up LFS and let it record a bunch of InSim data straight onto disk.
You then just create a mock socket object, that instead of receiving socket data from LFS, just reads the data from the file. You can read the file data exactly as you read InSim packets, it's all just a stream of bytes.
This way it's very easy to test whole races, as it only takes a second or so for a modern PC to read a 40 lap race from disk, instead of having to go through the hassle of creating a socket, connecting to InSim and playing back a replay.
I don't have a Spark example of a warm-up lap, but I have written such an app before. I used this logic for it.
- Log player start position (IS_REO at race start)
- Send message to tell player to follow warmup lap rules (speed limit and no overtaking)
- Check player speed and position each update
- Check if player exceeds speed limit or player position decreases (decrease means overtaking other car)
- Spectate player and send appropriate error message
- When lead car finishes first lap send "green flag" message and stop tracking
Yes, this was an issue in some early examples, as it's possible to get MCI updates before the NCN or NLP packets have been processed. However all of the recent examples include logic to prevent this from happening.
static void MultiCarInfo(IS_MCI mci) { foreach (var car in mci.CompCars) { IS_NPL npl; // Get the NPL packet if it exists... if (_players.TryGetValue(car.PLID, out npl)) { var kph = MathHelper.SpeedToKph(car.Speed); if (kph > 80) { _insim.Send("/spec {0}", npl.PName); _insim.Send("{0} ^3spectated for speeding", npl.PName); } } } }
Yeah, I've found that very useful in the past. Record some raw InSim data to file and then test against that. From file you can test a whole 40 lap race in less than a second.
I've received a couple of questions about using Spark within a GUI application, and while I plan to release a Win32 example in the next build, the premise is very simple. A GUI app with Spark is exactly like a console app, except you don't need to call void InSim.Run(), as the program stays open regardless, however you do need to deal with cross-thread operations. As Spark is a multi-threaded library, you need to syncronize Spark with the main GUI thread in order to perform any updates. There are a bunch of ways of doing this, but I like to use delegates and lambdas for the task.
class MainForm : Form { void DoInvoke(Action action) { // Check if invoke is needed. if (InvokeRequired) Invoke(action); // Invoke action. else action(); // No invoke needed. }
// Called when an MSO packet is received. void MessageReceived(IS_MSO mso) { // Invoke Spark onto the main GUI thread. DoInvoke(() => { // Perform any updates. Try to keep them small. _messageTextBox.Text += mso.Msg + Environment.NewLine; }); } }
As I said the next release will include both Win32 and WPF examples of using the library.
There is no built-in support for MySQL in .NET, you need to use a third party library for it, so I'm not sure exactly what the performance would be. I would imagine that with MySQL the main bottleneck would be accessing the database itself, regardless of what language you were using.