The online racing simulator
Ugh... as usual I'm having trouble setting this library up in Eclipse. I added the external JAR file, copied code from the examples, compiled to JAR, tried to launch it using "java Compiled.jar" and it says "Error: Could not find or load main class VortexDrift".

Any ideas?
I don´t know how your MANIFEST.MF is set. But try to run it by command java -jar Compiled.jar
Now I get this error:
Exception in thread "main" java.lang.NullPointerException
at sun.launcher.LauncherHelper.getMainClassFromJar(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

Anyone mind setting up an Eclipse project with the helloworld example for me?
Alright, I followed everything in those websites and I still get exceptions...

C:\>java -jar Vortex.jar
Exception in thread "main" java.lang.NoClassDefFoundError: net/sf/jinsim/respons
e/InSimListener
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
at java.security.SecureClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.defineClass(Unknown Source)
at java.net.URLClassLoader.access$100(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: net.sf.jinsim.response.InSimListene
r
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 13 more

Send me or upload somewhere your wortex.jar file. I take a look at it.
Here it is
Attached files
Vortex.zip - 2.2 KB - 594 views
In your jar file is only Main.class so you must have another libs in directory with vortex.jar. I had to add jinsim and commons-login libraries and edit META-INF\MAINFEST.MF file.


Manifest-Version: 1.0
Main-Class: vortexDrift.Main
Class-Path: [B]jinsim-0.5.jar commons-logging-1.1.1.jar[/B]

Now is possible to run it by command java -jar vortex.jar without visible errors.

If you have not Class-Path property in MANIFEST.MF you must run it by command java -cp jinsim-0.5.jar;commons-logging-1.1.1.jar;vortex.jar vortexDrift.Main
Attached files
Vortex.zip - 228.8 KB - 609 views
Alright, I've finally got it to start.

Another problem I'm having is I'm getting an exception whenever I receive a packet:
Mar 15, 2012 7:00:53 PM net.sf.jinsim.AbstractChannel run
SEVERE: Something went wrong!
java.lang.NullPointerException
at net.sf.jinsim.SimpleClient.notifyListeners(SimpleClient.java:23)
at net.sf.jinsim.AbstractChannel.run(AbstractChannel.java:112)
at java.lang.Thread.run(Unknown Source)

This happened with my newly exported JAR and also with the one you provided. Any ideas?
I think, that you didn't register any listener to SimpleClient instance.
Just look at examples, there are many of its in JInsim lib
Quote from HonzaNB :I think, that you didn't register any listener to SimpleClient instance.
Just look at examples, there are many of its in JInsim lib

That was it, didn't spot it when I was writing my own code when I took helloworld as an example. Cheers, I'm all sorted now!
Alright, so today I got another exception:
Mar 19, 2012 6:20:26 PM net.sf.jinsim.AbstractChannel run
SEVERE: Something went wrong!
java.lang.IllegalArgumentException: The specified id is not a packet type id: 55
at net.sf.jinsim.PacketType.getPacket(PacketType.java:76)
at net.sf.jinsim.response.ResponseFactory.getPacketData(ResponseFactory.java:130)
at net.sf.jinsim.AbstractChannel.run(AbstractChannel.java:106)
at java.lang.Thread.run(Unknown Source)

Seems like the new Admin Command Report packet isn't added to the library:
ISP_ACR, // 55 - info : admin command report

Can anyone add it? Because if it's not added, my InSim app crashes
I saw that you changed getConnectionId() to a int value.

Most setConnectionId() functions have also been changed except for the ButtonRequest.setConnectionId() function, which still uses a byte

I've added a cast in my application but it shouldn't be like that methinks.

EDIT: My app also started throwing exceptions with the new JInSim:
Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
at net.sf.jinsim.AbstractChannel.<clinit>(AbstractChannel.java:18)
at jCruise.Main.Init(Main.java:45)
at jCruise.Main.main(Main.java:66)
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
at java.net.URLClassLoader$1.run(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 3 more

Seems like I'm missing the slf4j JAR file. I've looked a couple of pages back and the links are dead, so I downloaded the SLF4J-1.6.4 binaries, and I'm unsure which JAR file I've to add since it has many
I've created an example using gradle (http://gradle.org/) that takes care of everything:

In the SVN repo you find it here: https://jinsim.svn.sourceforge ... nsim/branch/0.6b/example/

After you checked out the example and installed gradle you can execute the command 'gradle run' in the example directory and the example is build and run.
The example is the Hello World example that connects to LFS in the local PC on Port 29999. If you want to change this in the example look for localhost an 29999 in the build.gradle file.

If you want to create a zip file with everything you need just type 'gradle distZip' and you find the created zip in build/distribution. Here also a .bat file is created to run the app.

To create a eclipse project just type 'gradle eclipse' and the project file are created that can be imported into eclipse.
Thanks for that, I'm all sorted again.

Seems like there is a mistake somewhere in JInSim. Whenever I try to send a message to a player using MessageToConnectionRequest, an exception is thrown:
java.lang.NullPointerException
at net.sf.jinsim.request.MessageToConnectionRequest.assemble(MessageToConnectionRequest.java:35)
at net.sf.jinsim.AbstractChannel.send(AbstractChannel.java:127)
at net.sf.jinsim.Client.send(Client.java:184)

You have used the wrong constructor that does not initialized the sound value.
I have fixed that now, and if you use the build.gradle from my previous post you should get the new version.
Cheers, it's all working now!
Here. I've built lastest version from SVN which Brilwing is using.
Attached files
jinsim-0.6.rar - 865.8 KB - 555 views
Hi,

I'm thinking of creating a request ID aware Client which would be able to register any number of "default" listeners (where reqI=0), and listeners with specific (non-zero) request IDs would only get the packets they are subscribed for.

I realize that QueueClient is the one for encapsulating the request ID handling, but it seems to assign only the first listener to reqI=0, but then it sends all reqI=0 packets to all the listeners.

What do you think?

Edit: I've come up with this class. Use it if you need and/or like it.

<?php 
package net
.sf.jinsim;

import java.io.IOException;
import java.util.ArrayList;
import net.sf.jinsim.request.InSimRequest;
import net.sf.jinsim.response.InSimListener;
import net.sf.jinsim.response.InSimResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * This Client handles both request and default listeners.
 * 
 * Request listeners
 * A request packet can be associated with a request listener via the send(packet,listener) method.
 * The request packet will be signed with a request ID.
 * Incoming packets with the same request ID are directed to the associated listener.
 * A maximum number of 255 listeners is possible to be associated at a time.
 * 
 * Default listeners
 * The send(packet) method can be used to send requests without associating listeners. 
 * Incoming packets with the default (0) request ID are directed to "default" listeners.
 * The number of default listeners is not limited.
 * 
 * @author szaboaz at freemail dot hu
 */
public class RequestClient extends Client {

    private static 
Logger log LoggerFactory.getLogger(RequestClient.class);
    
//for reqI = 0
    
private ArrayList<InSimListenerdefaultListeners;
    
//for reqI != 0
    
private InSimListener[] requestListeners;

    public 
RequestClient() {
        
defaultListeners = new ArrayList<InSimListener>();

        
// "0" is taken as "default" from the byte type's 256 different values,
        // so 255 other values remain. 
        
requestListeners = new InSimListener[255];
        
        
// request ID = (array index + 1)
    
}

    public 
void addDefaultListener(InSimListener listener) {
        
defaultListeners.add(listener);
    }

    public 
void removeDefaultListener(InSimListener listener) {
        
defaultListeners.remove(listener);
    }

    public 
void addRequestListener(InSimListener listener) {
        
        
// search for the next empty space in the array
        
int arrayIndex 0;
        while (
requestListeners[arrayIndex] != null) {
            
arrayIndex++;
        }
        if (
arrayIndex 254) {
            throw new 
IndexOutOfBoundsException("Maximum number of listeners exceeded!");
        }

        
requestListeners[arrayIndex] = listener;

        if (
log.isDebugEnabled()) {
            
log.debug("Added " listener.getClass().getName() + " to requestListeners at index " arrayIndex);
        }
    }

    public 
void removeRequestListener(InSimListener listener) {
        if (
listener != null) {
            for (
int i 0255i++) {
                if (
listener.equals(requestListeners[i])) {
                    
requestListeners[i] = null;
                }
            }
        }
    }

    @
Override
    
public void notifyListeners(InSimResponse packetData) {
        
int requestID packetData.getRequestInfo();

        
log.debug("\nIncoming packet " packetData.getClass().getName());
        
log.debug("RequestID: " requestID);

        if (
requestID == 0) {
            for (
InSimListener listener defaultListeners) {
                if (
listener != null) {
                    
log.debug("Packet goes to default listener: " listener.getClass().getName() + "\n");
                    
listener.packetReceived(packetData);
                } else {
                    
log.debug("Listener is null!\n");
                }
            }
        } else {
            
InSimListener listener requestListeners[requestID 1];
            if (
listener != null) {
                
log.debug("Packet goes to request listener: " listener.getClass().getName() + "\n");
                
listener.packetReceived(packetData);
            } else {
                
log.debug("Listener is null!\n");
            }
        }
    }

    @
Override
    
public void send(InSimRequest packetthrows IOException {
        
log.debug("Sending out packet.");
        
super.send(packet);
    }

    public 
void send(InSimRequest packetInSimListener listenerthrows IOException {
        
//let's get the array index of that listener
        
if (listener == null) {
            
log.debug("Listener is null!");
            return;
        }

        
int arrayIndex 0;
        while (!
listener.equals(requestListeners[arrayIndex])) {
            
arrayIndex++;
        }
        
//at this point, arrayIndex is the index of our listener
        //and since requestID = array index + 1:
        
byte requestID = (byte) (arrayIndex 1);
        
        
log.debug("Sending out packet with requestListener " listener.getClass().getName() + " Index: " requestID);
        
packet.setRequestInfo(requestID);
        
super.send(packet);
    }
}
?>

Edit: as my understanding grows, I see how my humble class above might not be adequate for general use. I'm pondering about other requests where request ID is prohibited from being zero, for example button requests.
Maybe I should fix the code above by sending all responses whose request ID is not zero, still don't have specific listeners assigned to them, so all those responses should still go to (all of the) default listeners.

Edit: I did just that, here's the current version:

<?php 
package net
.sf.jinsim;

import java.io.IOException;
import java.util.ArrayList;
import net.sf.jinsim.request.InSimRequest;
import net.sf.jinsim.response.InSimListener;
import net.sf.jinsim.response.InSimResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * This Client handles both request and default listeners.
 * 
 * Request listeners
 * A request packet can be associated with a request listener via the send(packet,listener) method.
 * The request packet will be signed with a request ID.
 * Incoming packets with the same request ID are directed to the associated listener.
 * If there are no listeners associated to the incoming packet's request ID, the packet
 * will be sent to all default listeners.
 * A maximum number of 255 listeners is possible to be associated at a time.
 * 
 * Default listeners
 * The send(packet) method can be used to send requests without associating listeners. 
 * Incoming packets with the default (0) request ID are directed to "default" listeners.
 * The number of default listeners is not limited.
 * 
 * 
 * @author szaboaz at freemail dot hu
 */
public class RequestClient extends Client {

    private static 
Logger log LoggerFactory.getLogger(RequestClient.class);
    
//for reqI = 0
    
private ArrayList<InSimListenerdefaultListeners;
    
//for reqI != 0
    
private InSimListener[] requestListeners;

    public 
RequestClient() {
        
defaultListeners = new ArrayList<InSimListener>();

        
// "0" is taken as "default" from the byte type's 256 different values,
        // so 255 other values remain. 
        
requestListeners = new InSimListener[255];
        
        
// request ID = (array index + 1)
    
}

    public 
void addDefaultListener(InSimListener listener) {
        
defaultListeners.add(listener);
    }

    public 
void removeDefaultListener(InSimListener listener) {
        
defaultListeners.remove(listener);
    }

    public 
void addRequestListener(InSimListener listener) {
        
        
// search for the next empty space in the array
        
int arrayIndex 0;
        while (
requestListeners[arrayIndex] != null) {
            
arrayIndex++;
        }
        if (
arrayIndex 254) {
            throw new 
IndexOutOfBoundsException("Maximum number of listeners exceeded!");
        }

        
requestListeners[arrayIndex] = listener;

        if (
log.isDebugEnabled()) {
            
log.debug("Added " listener.getClass().getName() + " to requestListeners at index " arrayIndex);
        }
    }

    public 
void removeRequestListener(InSimListener listener) {
        if (
listener != null) {
            for (
int i 0255i++) {
                if (
listener.equals(requestListeners[i])) {
                    
requestListeners[i] = null;
                }
            }
        }
    }

    @
Override
    
public void notifyListeners(InSimResponse packetData) {
        
int requestID packetData.getRequestInfo();

        
log.debug("\nIncoming packet " packetData.getClass().getName());
        
log.debug("RequestID: " requestID);

        if (
requestID == 0) {
            for (
InSimListener defaultListener defaultListeners) {
                if (
defaultListener != null) {
                    
log.debug("Packet goes to default listener: " defaultListener.getClass().getName() + "\n");
                    
defaultListener.packetReceived(packetData);
                } else {
                    
log.debug("Listener is null!\n");
                }
            }
        } else {
            
InSimListener listener requestListeners[requestID 1];
            if (
listener != null) {
                
log.debug("Packet goes to request listener: " listener.getClass().getName() + "\n");
                
listener.packetReceived(packetData);
            } else {
                
log.debug("No request listener is associated with this Request ID!\n");
                for (
InSimListener defaultListener defaultListeners) {
                    if (
defaultListener != null) {
                        
log.debug("Packet goes to default listener: " defaultListener.getClass().getName() + "\n");
                        
defaultListener.packetReceived(packetData);
                    } else {
                        
log.debug("Listener is null!\n");
                    }
                }
            }
        }
    }

    @
Override
    
public void send(InSimRequest packetthrows IOException {
        
log.debug("Sending out packet.");
        
super.send(packet);
    }

    public 
void send(InSimRequest packetInSimListener listenerthrows IOException {
        
//let's get the array index of that listener
        
if (listener == null) {
            
log.debug("Listener is null!");
            return;
        }

        
int arrayIndex 0;
        while (!
listener.equals(requestListeners[arrayIndex])) {
            
arrayIndex++;
        }
        
//at this point, arrayIndex is the index of our listener
        //and since requestID = array index + 1:
        
byte requestID = (byte) (arrayIndex 1);
        
        
log.debug("Sending out packet with requestListener " listener.getClass().getName() + " Index: " requestID);
        
packet.setRequestInfo(requestID);
        
super.send(packet);
    }
}
?>

I've just realized another possible shortcoming: what if I want to use the same request ID with two different listeners. Not possible at the moment.
Of course if an incoming packet comes with a request ID, the same listener can handle different things... Still...

Edit: Okay, here's a new version. Any number of listeners can be assigned to any of the request IDs. It's more elegant too, without that ugly array
For convenience, there're methods for adding and removing listeners to all the request IDs.


<?php 
package net
.sf.jinsim;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import net.sf.jinsim.request.InSimRequest;
import net.sf.jinsim.response.InSimListener;
import net.sf.jinsim.response.InSimResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * You can subscribe to this Client any number of
 * - request ID specific listeners (which listen only to responses
 *   with the specified IDs).
 * - general listeners (which listen to responses with any request IDs),
 * 
 * Specified request ID values must be in the range of 0 to 255.
 * 
 * @author szaboaz at freemail dot hu
 */
public class RequestClient extends Client {

    private static 
Logger log LoggerFactory.getLogger(RequestClient.class);
    private 
ArrayList<HashSet<InSimListener>> listeners;
    
    public 
RequestClient(){
        
listeners = new ArrayList<HashSet<InSimListener>>();
        for (
int i=0i<=255i++){
            
listeners.add(new HashSet<InSimListener>());
        }
    }
    
    public 
boolean addListener(InSimListener listener) {
        
boolean returnValue true;
        for (
int i=0i<=255i++){
            
returnValue listeners.get(i).add(listener);
        }
        return 
returnValue;
    }

    public 
boolean addListener(InSimListener listenerint requestID) {
        if (
requestID >= && requestID <=255){
            return 
listeners.get(requestID).add(listener);
        }
        else {
            return 
false;
        }
    }
    
    public 
boolean removeListener(InSimListener listener) {
        
boolean returnValue true;
        for (
int i=0i<=255i++){
            
returnValue listeners.get(i).remove(listener);
        }
        return 
returnValue;
    }
    
    public 
boolean removeListener(InSimListener listenerint requestID) {
        if (
requestID >= && requestID <=255){
            return 
listeners.get(requestID).remove(listener);
        }
        else {
            return 
false;
        }
    }
    
    @
Override
    
public void notifyListeners(InSimResponse packetData) {
        
int requestID packetData.getRequestInfo();
        
        
HashSet<InSimListenerlistenerSet listeners.get(requestID);
        for (
InSimListener listener listenerSet){
            
listener.packetReceived(packetData);
        }
    }
    
    public 
void send(InSimRequest packetint requestIDthrows IOException {
        
packet.setRequestInfo((byte)requestID);
        
send(packet);
    }
    
}
?>

Why do TinyRequest and SmallRequest default to RequestInfo (reqI) 254?

Edit: oh, I can see it now. LFS reports "InSim : TINY_PING with no ReqI - QuickSpec" if it was sent with reqI = 0. Maybe it's there somewhere in the documentation?

Edit: ah, there it is in InSim.txt:
// ReqI : non-zero (returned in the reply)
// SubT : TINY_PING (request a TINY_REPLY)

Now I just need to find out, why is it 254 (why not 255, e.g.) ?
I have some ideas so I write them here mainly for the record, but comments or guidance are welcome.

Shouldn't the client fire some event when its channel goes down?
Edit: When I was using UDPChannel, and I closed LFS, my connected client's isConnected still reported "true". No such problem with TCPChannel.

Maybe regular (re)connection attempts could also be part of the library.

Edit:
Also, keeping an up-to-date list of currently connected people with on-track off-track status indication seems like a common requirement. (And not a trivial one at that, AFAICT.)

Edit:
Message/names encoding-decoding (codepages, color codes, escaped characters), maybe it's in there, I just haven't stumbled upon it yet.

Edit:
I've not only found the Encoding class, but already caught a bug (missing feature) in it. There's this line in encodeString(String text) method:

<?php 
if (ch && ch 128) {
?>

This exludes the zero character '\0' a.k.a. '\u0000', which is a problem, because zero characters are needed for initializing text in button text entry dialogues. Excerpt from insim.txt:
// On clicking the button, a text entry dialog will be opened, allowing the specified number of
// characters to be typed in. The caption on the text entry dialog is optionally customisable using
// Text in the IS_BTN packet. If the first character of IS_BTN's Text field is zero, LFS will read
// the caption up to the second zero. The visible button text then follows that second zero.

// Text : 65-66-67-0 would display button text "ABC" and no caption

// Text : 0-65-66-67-0-68-69-70-71-0-0-0 would display button text "DEFG" and caption "ABC"

I'm inclined to change that line to this:

<?php 
if (ch 128) {
?>

This includes 0 (and no need to check the lower boundary, since chars' minimum value is zero). I wonder what was the reason behind excluding it in the first place...
Some handy tips for button users (maybe this could go into net.sf.jinsim.examples.button.Main):

When you want to create text entry dialogs (typeIn buttons), and initialize them with some text at the same time, you can do this (in Java 7 you can use binary literals, very handy when dealing with bit patterns):


//Set the texts
//this will be on the button, and also in the text entry dialog as initialized text
String typeInText = "typeInText";
//the title of the text entry dialog
String buttonTitleText = "titleText";
buttonRequest.setText("\u0000"+buttonTitleText+"\u0000"+typeInText);

//Enable typeIn button
//This would enable typeIn type button, but nothing appears in the text entry box
//because the character number has to be larger than the length of typeInText
//buttonRequest.setTypeIn( (byte) 0b1000_0000 ); //0 character

//This is the maximum value
buttonRequest.setTypeIn( (byte) 0b1101_1111 ); //95 character

And for the sake of nice readable code, I can do it like this:

byte TYPE_IN = (byte) 0b1000_0000;
//Or without binary literal:
//byte TYPE_IN = (byte) -128;
int typeInTextLength = 95;
buttonRequest.setTypeIn( (byte)(typeInTextLength | TYPE_IN) );


FGED GREDG RDFGDR GSFDG