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
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 data
static 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
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
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
How to do Control Flow via Polymorphism
Let's say you have n objects implementing an interface. Call these message, implementing IMessage. Each message may cause a different action when given to another object, the receiver. Sounds like the perfect scenario for Polymorphism. Create a method Handle() for each of the n messages specifying the concrete type in the signature. Now, let's say you receive that message from a MessageFactory and, obviously, that factory hands you the message via the interface. So here's the pseudo code:
public interface IMessage
{
}
public class MessageA : IMessage
{
}
public class MessageB : IMessage
{
}
public class Receiver
{
public void Handle(MessageA msg)
{
}
public void Handle(MessageB msg)
{
}
}
public class MessageFactory
{
public static IMessage GetMessage()
{
}
}
Now, how do you pass this message off to the appropriate Handle() version? You can't just call Handle() with your message because you message is actually an IMessage not the concrete type. I can think of three workarounds and don't like any of them. Is there another way, or is this just a limitation of C#? In the meantime, here are the three ways I can think of solving this:
The Switch
The traditional way, use if/else or switch to examine the incoming object and dispatch it. Just create a method Handle(IMessage msg) and have it dispatch each to the proper Handle(). Traditional, simple. But as n increases it becomes less and less readable and dispatch is now a runtime decision. It could just fail, if you overlooked a concrete class.
The Reflection
At runtime examine the actual type of the message and then inspect your receiver to dynamically invoke the appropriate methods. No worries about maintaining the code or readability. It's just magic... but runtime magic which may yet fail if you overlooked a concrete class.
The Self-Dispatcher
There is probably a named pattern that goes with this approach. It shares some aspects with the Visitor pattern (that's what inspired me actually), but I don't know what this pattern is actually called. The idea is that each message doesn't get passed to the receiver but passes itself. I.e. it calls the appropriate method. For this to work, we add a method to our interface:
public interface IMessage
{
void Dispatch(Receiver receiver);
}
public class MessageA : IMessage
{
public void Dispatch(Receiver receiver)
{
receiver.Handle(this);
}
}
This works perfectly. After getting the message from the factory, you just call msg.Dispatch(receiver). Since each message implements its own dispatch for itself, we get compile time checking. Yay. But we basically copy and paste the Dispatch() method into every message class and we can't factor it into a common base-class, because that would break the flow control again. So it achieves the ends I'm after, but I don't like the implementation.
What other options are there?
Labels: .net, c#, control flow, polymorphism
Generic return values and explicit casting
Came across an artifact with generics that confused me. Let's say you had an interface
IFoo:
public interface IFoo
{
[...]
}
and a class
Foothat implements
IFoo:
public class Foo : IFoo
{
[...]
}
then a given method returning
IFoo doesn't have to explicitly cast
Foo to the interface:
public IFoo Foo()
{
return new Foo();
}
So, I figured that the same would be true for a generic method returning
T with a constraint to
IFoo. However, it seems that the constraint is not considered for implicit casting to
T. So you have to explicitly cast
Foo to
IFoo and then cast that to
T:
public T Foo<T>() where T : IFoo
{
return (T)(IFoo)new Foo();
}
That just seems a bit strange to have to do.
Eclipse "resource out of sync" needs refresh
Ok, I've now twice wasted time on trying to figure this out and this time I'm writing it down, so I don't do it a third time :)
I recently added an eclipse project to SVN (it's already in perforce, my usual SC of choice) and after that my JAR export would fail with a bunch of "Resource is out of sync" errors. Since all the resources were .svn related, I blamed SVN and chased this down the wrong path for a bit.
Here's the simple solution: If you see a jar export fail because of "resource is out of sync", try doing a Refresh on the project before anything else. That should solve it.
ISecurableChannel in TcpChannel not ready on mono
I was just trying to move a remoting service from .NET 2.0 to mono and ran into problems with Interop. The client & server would work fine if running on the same architecture, but fail when crossing architectures. Turns out that the secure portion of TcpChannels isn't completed yet on mono, throwing an
AuthenticationException when a Windows client tried to connect to a Linux server. The culprit is
ISecurableChannel. To avoid this, simply, don't use the security feature for now (i.e. use at own risk, best on a private net).
To get it working replace
ChannelServices.RegisterChannel(chan, true);
with
ChannelServices.RegisterChannel(chan, false);