Monday, September 10, 2007
Monday, August 20, 2007
LFSLib 0.16b w/ InSim Relay support released
With this version, LFSLib.NET gains support for the InSim Relay currently in testing (see this Forum thread for details). The InSim relay allows you to get a listing of servers connected to the relay and send and receive InSim packets to them without having to directly connect to them. To create an InSimHandler to the relay simply call:
IInSimRelayHandler relay = InSimHandler.GetMasterRelayHandler();
This server isn't up yet, so for testing Victor has set up the following server:
IInSimRelayHandler relay = InSimHandler.GetRelayHandler("vic.lfs.net", 47474);IInSimRelayHandler implements a subset of the main InSimHandler, discarding methods, properties and events that don't make sense and adding a couple of relay specific ones.
The remainder of the changes are minor tweaks and bug fixes:
- Added auto-reconnect feature to handler (really only works for TCP, since UDP is connectionless)
- CameraPositionInfo now allows ShiftU & related properties to be set
- Updated TrackingInterval to accept values as low as 50ms
- BUGFIX: Fixed race condition where a LFSState event could be fired on a separate thread before the main thread noticed that LFS was connected, allowing for invalid NotConnected exceptions to occur.
- BUGFIX: Fixed Ping event (was looking for the wrong packet)
- BUGFIX: RaceTrackPlayer.HandicapIntakeRestriction always returned zero
Full details are in the release notes.
All links, docs, etc are at lfs.fullmotionracing.com
Labels: InSim Relay, LFS, LFSLib.NET, live for speed
Tuesday, July 31, 2007
LFSLib.NET 0.15b released
Although 0.15b was supposed to be the version that brings LFS proxy support to LFSLib.NET, a crash-bug with AXI & AXO packets forced me to rollback that incomplete code for now and release a bugfix version.The bugs fixed in this version are:
- AXI & AXO packets were not properly received and killed the TCP handler
- Eliminated race condition in static population of encodings
- Fixed bug with text-less button
- Socket clean-up in UDP handling could throw unexpected exceptions
- OutSim & OutGauge used bad buffer sizes if ID was 0 and would throw an OutOfRangeException
- Close() could throw NotConnected exception, which was useless
The remainder of the changes are clean-up and non-breaking, forcing some more parts of the API into obsolescence.
Full details are in the release notes.
All links, docs, etc are at lfs.fullmotionracing.com
Labels: LFS, LFSLib.NET, live for speed
Monday, July 30, 2007
Playing with Xaml and LFS
I thought it was about time to see how hard it would be to get Live For Speed Track Maps into Xaml for use on the desktop or Silverlight. I initially started with the LFS's SMX (simple mesh format) files, but taking triangle data and turning it into a manageable number of polygons for vector representation is proving to be an interesting exercise. I might get back to it later, but for now, have found a more productive approach.LFS provides a file format called PTH which basically contains the drive line in world coordinates as a set of nodes for each track configuration. But here's cool part: Every node also includes orientation and the left and right distances from the node to the track edges and drivable area edges. Given the left and right edges from two nodes describes a rectangle. I decided to render this out as a series of polygons in Xaml and the results was a very usable representation of the track.
But this still generates a lot of polygons for such a relatively simple shape. I initially went down the path of just skipping over nodes and doing a reduction of detail that way. This approach, however, quickly deteriorated the detail significantly. My next approach was to take the edges of multiple node polygons and simply draw a single polygon encompassing them all. The file size didn't drop as significantly, but the detail didn't drop at all. Below is the comparison of the two polygon reduction techniques:
| Skipped Node Polygon Reduction | Edge following Polygon Reduction | |
|---|---|---|
|
2 nodes/polygon At this stage the two outputs are identical. And xaml files sizes are identical at about 90KB. |
|
|
|
4 nodes/polygon You can see a little detail reduction in the skipped node version. But file size reductions are drastic. 31KB for the skipped version, 40KB for edge-following. |
|
|
|
8 nodes/polygon Now, the level of detail reduction in the skipped version is becoming noticable. Since this is a vector format it lends itself to panning and zooming, but at this point the loss of detail would become annoying when zoomed. However, file size reductions continue to out pace the edge following with 14KB and 27KB respectively. |
|
|
|
16 nodes/polygon At this stage the skipped node format is basically unusable as a representation, while the edge following still looks as crisp as the original. File size for skipped has again halved, but really is not useful. Edge following is at 21KB, clearly slowing down in file size reduction |
|
|
I continued with the edge following polygon reduction technique up to 128 nodes per polygon. However by 64, the file wasn't getting any smaller anymore (the verbosity of Xaml was negligible compared to the number of polygon points. The best final file size was about 17KB, which is quite acceptable for a Silverlight application, if you ask me.
Next I need to hook this Xaml up to my InSim code and then I'll be able to render race progression. This'll have to be inside of a WPF app for now, until Silverlight's CLR gets Sockets and I can port LFSLib.NET to it. Right now, i'd have to do constant server callbacks which wouldn't scale or perform well.
Labels: LFS, LFSLib.NET, live for speed, silverlight, xaml
Friday, July 27, 2007
Lfs State Inspector
I've been spending a lot of time, just firing up LFS, hopping through screens and catching the state events. But there's a lot of state and no good way to see what's changed (most is obvious, but some of it isn't). So i threw together a quick state inspector app that just reports the current state and uses color highlights what values have changed. In addition, the shade of the color indicates how many state changes ago it happens, as there are certain actions that cause multiple state change events to be sent and then it's easy to miss a change.
I figure, it's also a nice simple example of LFSLib.NET, so i'm releasing it both as binary and source for people to play with.
Labels: LFS, LFSLib.NET, live for speed
Monday, July 16, 2007
Time Monitor example
There was a request for a Time Monitor app that would show splits and the lap time including delta's. Currently LFS, shows you your splits and laps, but only briefly and without any information about how it compares to the previous lap. But with the LFS button system, custom HUD elements can now be created relatively simply. Really, "Button" is a misnomer. It's really a label control that can be set to clickable.I figured the time monitor would be a good example to use the RaceTracker class I wrote a couple of days ago and exercise the button system to display the data gathered by the tracker. The resulting app, is a relatively simple winforms sample (the UI exists purely for configuration) that produces LFS stats like this (click image for full screenshot):
The UI for the app itself just lets the user specify how to connect to LFS
The app is not meant for LFS servers, but as a local Aid for clients. It connects to a client instance and displays the buttons locally, according to the currently viewed player Id. I.e. it switches the hud as you tab through the players. It won't start displaying anything until it hits a first split (i.e. if you just connected and the viewed player has already past the first split, no statistics for the player will show up until the next split 1 pass.
The code is a Visual Studio Solution, created in VS 2005, although it should work fine in Visual Studio Express. It can be downloaded from here. It's written in C# and uses the LFSLib.NET library for InSim connectivity. It's not meant to be an App, and you won't get any support from me for the binary. You can run it as is, but it's meant to show how LFSLib.NET can be used for tracking and creating buttons. As such, this is example code and completely free of any license (LFSLib.NET is still governed by the GPL, though). Use it however you wish.
Labels: LFS, LFSLib.NET, live for speed, winforms
Saturday, July 14, 2007
OutGauge Monitor Windows App example
I had a request for an example of Win Forms with OutGauge. This is a very simple project that illustrate how to quickly wire up output from OutGauge to UI. The only tricky bit for novices is the whole Invoke business. I.e. updates from outgauge come from it's listener thread. So you cannot use the event handler for outgauge to directly update your UI, or you'll get Cross-Thread exceptions. The solution is the common Invoke Required boilerplate:private void outGaugeHandler_Updated(object sender, Gauge gauge) { // this code is required for Windows Forms, since our OutGauge update // comes from another thread and we can only perform form updates on // the UI thread if (this.InvokeRequired) { BeginInvoke(new OutGaugeHandler.GaugeEvent(outGaugeHandler_Updated), sender, gauge); return; } // now we update the form with the new data // text boxes timeTextBox.Text = gauge.Time.ToString(); carTextBox.Text = gauge.Car; [...] }
The resulting App, has a single window that refreshes OutGauge updates at the interval specified in the Live For Speed cfg.txt.
The Solution for this project is for Visual Studio 2005, but should work just fine in Visual C# Express. The zip of the project can be found here
Update: LFSLib.NET 0.14b has a bug where an OutGauge ID 0 zero would cause an exception. You can avoid this by using an OutGauge ID higher than 0 or wait for 0.15b to be released. Same thing is true for OutSim.
Labels: LFS, LFSLib.NET, live for speed, winforms
Thursday, July 12, 2007
A simple race tracking class for LFSLib.NET
Continuing in providing some usage examples for LFSLib.NET, I've created a very simple race tracking class. I figure it covers a lot of ground, since most people want to keep track of race progress, and it does show the basic use of the InSimHandler.This class only tracks players entering and leaving and reports their split and lap times. It ignores player renames and other race information, and just spits that info out to the console. There is a static RaceTracker.Test() method that's just to be able to run the class from your Main, i.e. it's not how you'd likely use it in your actual tracking code.
using System; using System.Collections.Generic; using System.Text; using FullMotion.LiveForSpeed.InSim; using FullMotion.LiveForSpeed.InSim.EventHandlers; using FullMotion.LiveForSpeed.InSim.Events; namespace FullMotion.LiveForSpeed { /// <summary> /// Simple Race Tracking example. Keeps track of player names /// and reports the split and lap times for each player /// </summary> public class RaceTracker { /// <summary> /// Just a static test call for running RaceTracker against a local LFS instance /// </summary> public static void Test() { RaceTracker tracker = new RaceTracker("127.0.0.1", 30000); tracker.Start(); Console.WriteLine("Press Enter to Exit"); Console.ReadLine(); tracker.Stop(); } InSimHandler handler; // state variables to make sure we don't start and stop // the handler multiple times bool started = false; bool running = false; // keep track of player names, since race tracking only gives us IDs Dictionary<byte, string> players = new Dictionary<byte, string>(); /// <summary> /// Set up an insim handler for tracking basic race info /// </summary> /// <param name="host"></param> /// <param name="port"></param> public RaceTracker(string host, int port) { handler = new InSimHandler(); handler.Configuration.LFSHost = host; handler.Configuration.LFSHostPort = port; handler.Configuration.UseTCP = true; handler.RaceTrackPlayer += new RaceTrackPlayerHandler(handler_RaceTrackPlayer); handler.RaceTrackPlayerLeave += new RaceTrackPlayerLeaveHandler(handler_RaceTrackPlayerLeave); handler.RaceTrackPlayerSplitTime += new RaceTrackPlayerSplitTimeHandler(handler_RaceTrackPlayerSplitTime); handler.RaceTrackPlayerLap += new RaceTrackPlayerLapHandler(handler_RaceTrackPlayerLap); } /// <summary> /// Connect to LFS and start tracking /// </summary> public void Start() { if (started) { throw new InvalidOperationException("RaceTracker cannot be started multiple times"); } handler.Initialize(); started = true; running = true; // make sure we get all players in the race handler.RequestPlayerInfo(); } /// <summary> /// Close down the connection /// </summary> public void Stop() { if (running) { handler.Close(); running = false; } } private void handler_RaceTrackPlayer(InSimHandler sender, RaceTrackPlayer e) { if (!players.ContainsKey(e.PlayerId)) { players.Add(e.PlayerId, e.Playername); Console.WriteLine("Player joined: {0} ({1})", e.Playername, e.PlayerId); } } private void handler_RaceTrackPlayerLeave(InSimHandler sender, RaceTrackPlayerLeave e) { Console.WriteLine("Player left: {0} ({1})", players[e.PlayerId], e.PlayerId); players.Remove(e.PlayerId); } void handler_RaceTrackPlayerLap(InSimHandler sender, RaceTrackPlayerLap e) { Console.WriteLine( "Player '{0}': Lap {1} @ {2}:{3}.{4}", players[e.PlayerId], e.LapsDone, e.LapTime.Minutes, e.LapTime.Seconds, e.LapTime.Milliseconds); } void handler_RaceTrackPlayerSplitTime(InSimHandler sender, RaceTrackPlayerSplitTime e) { Console.WriteLine( "Player '{0}': Split {1} @ {2:00}:{3:00}.{4:000}", players[e.PlayerId], e.Split, e.SplitTime.Minutes, e.SplitTime.Seconds, e.SplitTime.Milliseconds); } } }
Sample output of this running against a race of three AIs on Aston Cadet Reverse looks like this:
Press Enter to Exit Player joined: AI 2 (6) Player joined: AI 3 (8) Player joined: AI 4 (10) Player 'AI 2': Split 1 @ 00:29.870 Player 'AI 4': Split 1 @ 00:31.280 Player 'AI 3': Split 1 @ 00:31.800 Player 'AI 2': Lap 1 @ 1:5.960 Player 'AI 4': Lap 1 @ 1:9.180 Player 'AI 3': Lap 1 @ 1:10.220 Player left: AI 2 (6) Player left: AI 3 (8) Player 'AI 4': Split 1 @ 00:26.410 Player left: AI 4 (10)
The Player left are me manual throwing the AIs into spectate mode.
The players dictionary could easily be changed to hold a Player class instead of string as the value slot and then more data can be tracked and even a continuous log of the race results could be kept. But this should be a place to start building a race tracking application
Labels: LFS, LFSLib.NET, live for speed
Tuesday, July 10, 2007
LFSLib.NET 0.14b released
UPDATE: If you downloaded the files before 12pm PST, then you may have a binary with a bug. Please get the current one.
This is is a minor bugfix/clean-up release. There were two bugs that were the result of TCP connections. In addition, I took the opportunity to clean up some naming conventions and odds and ends. This should be a non-breaking upgrade, although a couple of members have been marked obsolete and will generate warnings until replaced with their successors.
Full details are in the release notes.
I've added a pure binary release of the lib, so you don't have to do digging through the output folders anymore, if all you want is a dll to reference. The binary release can be found here.
All links, docs, etc are at lfs.fullmotionracing.com
Labels: LFS, LFSLib.NET, live for speed
Thursday, June 28, 2007
LFSLib.NET 0.13b released
Now with full patch X goodness!This version of LFSLib.NET takes advantage of all the new features of Live For Speed S2 0.5X (as well as the X2 test patch additions). The new version can be found here and the release notes are here. There is a new binary distro with a winforms Tester app, which is located here, but in general the tester app does not provide full coverage and will eventually be replaced with a better example and test harness. The highlights are:
- TCP connections (more on that below)
- Multiple InSim Connections at the same time
- Custom Button system
- Lots more tracking events
I've run it through its paces, but as InSim coverages is getting quite large now and I don't have an automated test suite, I cannot claim to have full test coverage. In general, if something doesn't work, assume it's LFSLib.NET, not LFS and let me know. Contact me via the email on the left, blog comments or posting about LFSLib.NET on the LFS programmer forums (which I monitor fairly consistently).
Moving the documentation to SandCastle has taken more time than I hoped. The new doc system is now a dynamic web application and provides a (IMHO) much improved interface thanks to Dave Sexton's excellent DocProject. This has required my moving of the docs off this server to a new host, so the docs and future information for LFSLib.NET from now on lives at lfs.fullmotionracing.com.
About TCP & UDP
A quick note about TCP and UDP in 0.13b. You can have up to 8 TCP connections, but you still can only have one UDP connection. When you configure the InSimHandler, set the UseTCP flag in the configuration. In general, you want to make sure that you do NOT set ReplyPort. Why? As soon as you set reply port, LFS will send RaceTrackNodelap (IS_NLP) and RaceTrackMultiCarInfo (IS_MCI) events via that port on UDP. LFSLib.NET handles that transparently, but since you've now crossed over into UDP land, you have now used up that one UDP connection that is available and things will get wonky if you connect another InSimHandler with the same settings, even though you thought you were just using TCP.Labels: .net, c#, InSim, LFS, LFSLib.NET, live for speed, OutGauge, OutSim
Wednesday, June 27, 2007
Simple OutSim & OutGauge samples
Version 0.13b is finished, just cleaning up the docs, and packaging everything (switching to sandcastle for docs kind of added a new complication). Should have that out tomorrow, I hope.In the meantime, especially, since the Tester app I include is really just something to exercise the lib, not to learn coding from (i.e. worst winforms code patterns example), I thought i'd throw out some simple code examples until i build a proper test suite. Now, these samples, with their use of straight up static methods are also nothing to learn program design from -- they're just API illustration. These examples work with the current and new release, so they're not tied to 0.13b.
The tricky thing to note about both OutSim and OutGauge is that the cfg.txt values take precedence. I.e. you can get both sent via InSim, but not if they are already set in the configuration file. Generally, I suggest sticking to manually configuring these two and going through the dedicated handlers for both and keeping them separate from your InSim handler.
OutGauge
This little Console app, just subscribes to OutGauge being sent once a second and refreshes the terminal with the new values:static void Main(string[] args) { // Assumes the following settings in LFS cfg.txt: // OutGauge Mode 1 // OutGauge Delay 100 // OutGauge IP 127.0.0.1 // OutGauge Port 26020 // OutGauge ID 1 OutGaugeHandler outGauge = new OutGaugeHandler(26020); outGauge.Updated += new OutGaugeHandler.GaugeEvent(outGauge_Updated); Console.WriteLine("created handler"); outGauge.Initialize(); Console.WriteLine("initialized handlers"); Console.WriteLine("Press RETURN to exit"); Console.ReadLine(); outGauge.Close(); } static void outGauge_Updated(object sender, FullMotion.LiveForSpeed.OutGauge.Events.Gauge gauge) { Console.Clear(); Console.WriteLine("OutGauge -----------------------"); Console.WriteLine("Time: {0}", gauge.Time); Console.WriteLine("ID: {0}", gauge.Id); Console.WriteLine("Car: {0}", gauge.Car); Console.WriteLine("RPM: {0}", gauge.RPM); Console.WriteLine("Speed: {0}", gauge.Speed); Console.WriteLine("Message1: {0}", gauge.DisplayMessage1); Console.WriteLine("Message2: {0}", gauge.DisplayMessage2); }
OutSim
The OutSim version is virtually the same for setup, just that the eventargs contain different datastatic void Main(string[] args) { // Assumes the following settings in LFS cfg.txt: // OutSim Mode 1 // OutSim Delay 100 // OutSim IP 127.0.0.1 // OutSim Port 26010 // OutSim ID 1 OutSimHandler outSim = new OutSimHandler(26010); outSim.Updated += new OutSimHandler.PhysicsEvent(outSim_Updated); Console.WriteLine("created handlers"); outSim.Initialize(); Console.WriteLine("initialized handlers"); Console.WriteLine("Press RETURN to exit"); Console.ReadLine(); outSim.Close(); } static void outSim_Updated(object sender, FullMotion.LiveForSpeed.OutSim.Events.PhysicsState physicsState) { Console.Clear(); Console.WriteLine("OutSim -----------------------"); Console.WriteLine("Time: {0}", physicsState.Time); Console.WriteLine("ID: {0}", physicsState.Id); Console.WriteLine("X: {0}", physicsState.PositionX); Console.WriteLine("Y: {0}", physicsState.PositionY); Console.WriteLine("Z: {0}", physicsState.PositionZ); }
Labels: LFS, LFSLib.NET, live for speed, OutGauge, OutSim
Thursday, June 21, 2007
Moonlight at Remix 07
Miguel de Icaza just posted a long entry on a Hackathon the mono team did in the last 21 days to get some version of Moonlight ready for Remix 07 and it looks like they succeeded. Further progress information can also be found on the Moonlight site.Next to the Silverlight 1.1 alpha which exposes the CLR, Moonlight is probably one of my favorite tech project to follow at this time. I don't even run X anywhere anymore, just using linux for servers, so I don't think i'll ever need Moonlight. But its the availability that has me excited. I am personally rooting for a web programming model that doesn't force javascript on you for client side programming, but I don't think Win/MacOS is "cross-platform" enough to establish this as a norm, rather than a specialty plug-in. I hope Moonlight tips the adoption likelihood in Silverlight's favor.
Currently, I'm mostly waiting for a Socket API to make it into Silverlight 1.1. Once that's available, I'm going to make sure that my LFSLib can run under silverlight. This would open the door for some very cool in-brower LFS admin tools. I also have an internal DirectX implementation of the LFS smx and pth formats for rendering top down views. I've been looking at porting that to WPF, since i don't required 3D and just rendering a plane at this point. If I can reduce it further to the Silverlight Xaml subset, I could easily produce a Silverlight equivalent of LFS Spectator. But I'm getting ahead of myself. Right now I just need to get full patch X support out the door for LFSLib.NET. The protocol is complete now, I just need to complete the TCP code, do some doc clean-up and testing.
Labels: LFS, LFSLib.NET, moonlight, silverlight
Monday, June 18, 2007
LFSLib.NET v0.12b (this is a test version)
I've just put v0.12b on the server. This version does bring LFS 0.5X compatibility, but it does not expose the new functionality. The goal of this version is primarily to let 0.11b code work against LFS 0.5X with the least amount of changes (some data just isn't represented the same anymore and caused breaking changes). This version is also ill tested, as I'm not going to go through an exhaustive set of tests until i get full 0.5X feature exposure (yeah, I know, i should be doing this test driven.. But that means a mock LFS or an LFS test harness and I haven't had the patience for that).So what has changed? Here are the notes:
- Moved to .NET 2.0 and Visual Studio 2k5
- Swapped out back-end for dealing with InSim packets to more closely resemble the native LFS formats and removed the intermediate Msg* classes (all internal changes)
- Finally mostly functional Unicode support for LFS message in and out transfers
- Updated existing functionality to work against patch X, with some breaking changes in what is available in Events and how that data is represented.
- Full patch X protocol implemented, just not yet exposed, so new packets don't come across as unknown packets
An extra note on "mostly function Unicode support". Basically for conversion of unicode to LFS encoding, I am using a lookup table from unicode to codepage+byte and there are a couple of LFS specific characters that screw up under Japanese encoding. And split messages have some issues. This will be fixed in 0.13b.
The new version can be found here and the home of the lib is here
Labels: InSim, LFS, LFSLib.NET, live for speed, patch X


