PDA

View Full Version : [HowTo] Companion App - UDP Streaming



Pages : 1 2 3 4 [5] 6

bendoe
02-01-2016, 14:27
I have a little playback app i use to assist me with testing that can playback recorded sessions from UDP or Shared Memory.

Thanks for that, but I wrote self a Test-App to analyze the data.


... as there is no error handling in the test app and it would probably just crash.
Why you don't implement a exception handling? Simple try-catch statement does wonder. :p


Just make sure you dont have anything else using port 5606 on your PC ...

Answer see my own Quote.


...
I get the packettype 0 that's all fine, but the packettype 1 is even missed.
...


I forgot to say, I've tested the singleplayer mode only! Maybe it work's with multiplayer, but that's not a solution for me.

cjorgens79
03-01-2016, 02:36
Why you don't implement a exception handling? Simple try-catch statement does wonder. :p


because its a waste of coding time (albiet a small one), its a hacked together test app I intended to only be used by myself to speed up my testing process by being able to replay recorded telemetry streams to my app. I know it wont fail on my pc, i only offered it to help you out and you come back criticising me for not going and catching every possible error for you. I told you what the pitfalls were so i didnt have to waste my time doing that.

bendoe
03-01-2016, 09:30
...
i only offered it to help you out ...

I write it one more, thanks for that!!!


...
... and you come back criticising me for not going and catching every possible error for you.

I think you don't understand what the smiley means with the tongue out!

mr_belowski
03-01-2016, 09:40
Think something may have got lost in translation somewhere. It did look like you were criticising his test app, even if that wasn't your intention. I wrote a similar tool which, like cjorgens' tool, had no error handling because I just didn't need that.

Back to the original question though, are you saying that your app receives the type 1 and 2 packets from a test sender, but not from the game?

pjrblue
03-01-2016, 11:25
I'm doing an android app for the fun of developing one, but I came to a dead end.
My app is working receiving data from the X1, but I cannot receive the data from the PS4, they are both in the same network and the HUD Dash free receive the data from both, only my app doesn't. Any idea?
I can also receive the data in the PC from both.



My code part for receiving the UDP

socketaddress = new InetSocketAddress(InetAddres.getByName("255.255.255.255"), 5606);
dSocket = new DatagramSocket(null);
dSocket = setReuseAddress(true);
dSocket = setBroadcast(true);
dSocket.bind(socketaddress);
dSocket.connect(socketaddress);

byte[] bytes = new byte[1367];
packet = new DatagramPacket(bytes, 0, bytes.length);
dSocket.receive(packet); <--- stops here

mr_belowski
03-01-2016, 11:35
I doubt it's related to your problem, but you don't need to explicitly set the broadcast address, broadcast flag, connect the socket - these are for sending. Here's part of my socket code -



DatagramSocket socket = new DatagramSocket(null);
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(5606));
byte[] recvBuf = new byte[2048];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(packet);


also the receive buffer size doesn't have to be exact - I used a size which I knew would be big enough

pjrblue
03-01-2016, 11:58
I doubt it's related to your problem, but you don't need to explicitly set the broadcast address, broadcast flag, connect the socket - these are for sending. Here's part of my socket code -



DatagramSocket socket = new DatagramSocket(null);
socket.setReuseAddress(true);
socket.bind(new InetSocketAddress(5606));
byte[] recvBuf = new byte[2048];
DatagramPacket packet = new DatagramPacket(recvBuf, recvBuf.length);
socket.receive(packet);


also the receive buffer size doesn't have to be exact - I used a size which I knew would be big enough

On PC I don't need, but on android if I don't use the specific "255.255.255.255" I don't get the packet.

mr_belowski
03-01-2016, 12:04
really? What's your android device?

Are you requesting a multicast lock and wifi high-performance mode? If you use the InetSocketAddress constructor with just the port, internally it'll use InetAddress.ANY

pjrblue
03-01-2016, 12:11
really? What's your android device?

Are you requesting a multicast lock and wifi high-performance mode? If you use the InetSocketAddress constructor with just the port, internally it'll use InetAddress.ANY

I have a Samsung Galaxy S2, Wiko Darkmoon and Nexus 7 2nd generation.

No I have not do the "multicast lock and wifi high-performance mode".

mr_belowski
03-01-2016, 12:17
I recommend you consider these - some devices won't receive broadcast packets without them both enabled

pjrblue
03-01-2016, 15:34
I recommend you consider these - some devices won't receive broadcast packets without them both enabled

I think I got it working.
I added the multitasklock and I was using one time only call for the "new DatagramSocket", and now I'm repeating that every time.

Thanks for the help.

The app running on my Samsung Galaxy S2 on the Xbox One, using a replay.

https://youtu.be/qrP6gyprW9g

mr_belowski
03-01-2016, 15:37
the multicast lock should be acquired once, when your app starts receiving the UDP data. Keep a reference to this lock object then release it when you've finished receiving data. For my app, I acquire it when the user clicks 'start' and the reference to that lock is a member variable of one of my classes. When the user clicks 'stop' I call .release() on that lock object. Same with the partial wake lock and wifi high-performance lock

Biddrace
03-01-2016, 18:09
CREW CHIEF: You will never updated with the Italian language ?:(

mr_belowski
03-01-2016, 18:20
No plans, sorry, its just too much work

Biddrace
03-01-2016, 18:22
OK thanks
pity,
It is a fantastic application:(:(

Tim Mann
04-01-2016, 16:53
As regards steering wheel buttons etc. I can get the data but finding somewhere to stick it all is somewhat problematic without breaking every ones apps. It would require an additional 16 bits (for all the buttons), or it can just map the first 16 buttons plus 8 more in dpad and top of mCrashData.

mr_belowski
04-01-2016, 17:28
aren't there a few spare bits in dPad and joyPad already? Isn't dpad just left/right/up/down?

I've got no issue with bunging it on top of CrashData, which is (as far as I know) unusable anyway

mr_belowski
04-01-2016, 17:31
While I got you Tim, any chance of more regular type 1 and 2 packets, and using that spare single bit in the sParticipantInfo sSector field to indicate whether this opponent car is the same class as the players? :)

Gassolini
04-01-2016, 18:01
Bumpty! (Have app ready...)


Is there any documentation detailing the units, range and unset values for the new telemetry parameters:

f32 sRideHeight[4];
f32 sSuspensionTravel[4];
f32 sSuspensionVelocity[4];
u16 sAirPressure[4];
u8 sWings[2];
f32 sEngineSpeed;
f32 sEngineTorque;

I'm guessing, m, m, m/s, milli bar, ?, ?, lbf/nm. Guesses aside, and yes, I can find out, it would be good to have it documented as for SharedMemory.

Also, what's sWings[] and EngineSpeed? Front and rear wing settings? Cranckshaft rad/sec?

cjorgens79
05-01-2016, 01:15
As regards steering wheel buttons etc. I can get the data but finding somewhere to stick it all is somewhat problematic without breaking every ones apps. It would require an additional 16 bits (for all the buttons), or it can just map the first 16 buttons plus 8 more in dpad and top of mCrashData.

Could we just map the wheel buttons completely over joyPad, and if the wheel also has a dPad (like the G27), map that into the dPad field? If the ingame controller settings were to use the wheel, then map wheel->joypad, else map gamepad->joypad. I would suspect most (if not all) who are using a wheel would want to map to buttons on their wheel rather than having to try and use gamepad for button mappings while they drive with a wheel, so imo the entire 16 bits of joypad can be overwritten when using a wheel. It would save you having to split into other vars.

oscarolim
05-01-2016, 09:51
Could we just map the wheel buttons completely over joyPad, and if the wheel also has a dPad (like the G27), map that into the dPad field? If the ingame controller settings were to use the wheel, then map wheel->joypad, else map gamepad->joypad. I would suspect most (if not all) who are using a wheel would want to map to buttons on their wheel rather than having to try and use gamepad for button mappings while they drive with a wheel, so imo the entire 16 bits of joypad can be overwritten when using a wheel. It would save you having to split into other vars.

I agree. This would make much sense and would not break any app out there.

jamesremuscat
05-01-2016, 17:18
I'm late to the party so might have missed it (100+ page thread!) but it looks like timing information is only available for the player's car, not the opponents, other than their most recent sector time. I'm thinking of the standard last lap, best lap, S1, S2, S3, pit stops.

That means it won't be possible to write a timing-screen type application. If so, that's a shame, because that's exactly the sort of app I'd love to write...

mr_belowski
05-01-2016, 17:24
You have to time the opponent laps yourself, using the sector numbers in the participantData to trigger your timer. It's a grotty solution but the only option with the current data layout

jamesremuscat
05-01-2016, 20:06
Couldn't you keep track of the reported sector times (sLastSectorTime) and which sector they were set in (sSector - 1 modulo 3), and hence deduce laptimes?

struct sParticipantInfo
{
s16 sWorldPosition[3]; // 0
u16 sCurrentLapDistance; // 6
u8 sRacePosition; // 8
u8 sLapsCompleted; // 9
u8 sCurrentLap; // 10
u8 sSector; // 11
f32 sLastSectorTime; // 14
// 16
};

(It looks like sLastSectorTime wasn't there in the first cut of the API...)

You'd start with a pretty sparse timing screen and wouldn't be able to be sure of personal bests if you joined partway through, but it would be more reliable than timing between packets...
Going to have to play around tonight I think!

cjorgens79
06-01-2016, 00:18
Couldn't you keep track of the reported sector times (sLastSectorTime) and which sector they were set in (sSector - 1 modulo 3), and hence deduce laptimes?


Yep you certainly can, thats how I'm doing it. Works a treat, the only pitfall is if you join a multiplayer session midway through, you have no way to determine the lap times those players achieved before you joined.

Here is a screenshot of my WIP live timing screen for pCars Dash using that data
224987

oscarolim
06-01-2016, 10:39
That's pretty much the only way to do it, and I think is an acceptable trade off (not having all the times when joining mid session).
That's the way I'm doing as well (not that is near completion), but from my testing is pretty accurate.

fsikansi
07-01-2016, 11:23
Hi, I started yesterday a mini project to read the UDP data with a Raspberry Pi and display some data in a LCD 2x16. For now I am just reading the UDP and I have a simple question about the "sPacketType", i don't know if I'm doing something wrong but I realize the this fields is "increased" every package, I thought that this field would have just values 0, 1 or 2. So I'm doing something wrong or this field have a "logic" that I'm missing?

jamesremuscat
07-01-2016, 11:30
I have a simple question about the "sPacketType", i don't know if I'm doing something wrong but I realize the this fields is "increased" every package, I thought that this field would have just values 0, 1 or 2. So I'm doing something wrong or this field have a "logic" that I'm missing?


This confused me for a little while before I spotted a comment somewhere in this thread that sPacketType is actually two fields in one byte: the bottom two bits are the packet type, and the other six are a packet sequence number.

So to get the packet type, you want to AND it with 0x3; to get the sequence number, (sPacketType & 0xFC) << 2.

This post in the middle of the thread has been very useful! http://forum.projectcarsgame.com/showthread.php?40113-HowTo-Companion-App-UDP-Streaming&p=1198591&viewfull=1#post1198591

Tim Mann
08-01-2016, 20:38
Next patch has a couple of minor changes. Car class same as player can be found at bit 4 (8) of participant info .sSector. Joypad now uses 'Focus Device' rather than default joypad. You'll find 16 buttons in .sJoypad, 4 more in the top of .sDPad and another 4 in the top of .sCrashState. Sorry I can't use player mappings (It's all to do with different levels of abstraction), but that covers 24 buttons from whatever device. There was a minor issue with packet types 1/2 being sent very irregularly in slow UDP modes, this has now been changed. Also it sends 1/2 at least every 10 seconds now. I'll update the first post when it goes live.

Tim Mann
08-01-2016, 20:52
Is there any documentation detailing the units, range and unset values for the new telemetry parameters:

f32 sRideHeight[4];
f32 sSuspensionTravel[4];
f32 sSuspensionVelocity[4];
u16 sAirPressure[4];
u8 sWings[2];
f32 sEngineSpeed;
f32 sEngineTorque;

I'm guessing, m, m, m/s, milli bar, ?, ?, lbf/nm. Guesses aside, and yes, I can find out, it would be good to have it documented as for SharedMemory.

Also, what's sWings[] and EngineSpeed? Front and rear wing settings? Cranckshaft rad/sec?

M
M
M2
KPA
Normalised final position in range
RPS
NM

Tim Mann
08-01-2016, 20:53
As regards DRS, anyone had a poke at the wing settings?

cjorgens79
09-01-2016, 02:02
Next patch has a couple of minor changes. Car class same as player can be found at bit 4 (8) of participant info .sSector. Joypad now uses 'Focus Device' rather than default joypad. You'll find 16 buttons in .sJoypad, 4 more in the top of .sDPad and another 4 in the top of .sCrashState. Sorry I can't use player mappings (It's all to do with different levels of abstraction), but that covers 24 buttons from whatever device. There was a minor issue with packet types 1/2 being sent very irregularly in slow UDP modes, this has now been changed. Also it sends 1/2 at least every 10 seconds now. I'll update the first post when it goes live.

When are these changes going to go live? I've just noticed I was using 4 bits for sector instead of 3, (didnt notice the comment said 3, assumed it was 4 + 4 for the extra precision of the xz position). At a bare minimum due to Apple's approval process it takes 2 weeks to get an update out on iOS, so if these changes were coming out before that, then I am going to have some unhappy iOS users with broken apps.

Also, are you able to post the updated joyPad/dPad mappings prior to the first post being updated?

Tim Mann
09-01-2016, 14:59
You can mask out the bits now, bit 3 isn't used currently, same with the extra joypad bits (they will currently be set to 0). As regards mapping, it's the same as the joypad for the normal buttons, then it will be device dependent.

mr_belowski
09-01-2016, 15:09
Brilliant news tim, these changes will make a big difference to the quality of the experience with apps using the stream.

I've not looked at the wing data, will have a fiddle later.

The engine speed, is this revolutions per second or radians per second?

oscarolim
09-01-2016, 15:11
And the suspension velocity can't be m2. Square meters is not a unit of speed.

mr_belowski
09-01-2016, 15:14
Typo should be m/s, surely :)

oscarolim
09-01-2016, 15:45
As regards DRS, anyone had a poke at the wing settings?

I've tested with the McLaren P1 and keeps returning 0 on both wings, unless I'm doing something wrong on the way to read it...
With the Formula A sends 127. Odd. I'm reading the same way I read tyre temperature for example (which is the same structure, array of floats) but one works and the other doesn't :s

EDIT: OK. The wings[] do change when I changed the level of downforce on the car setup, but don't change when I activate the DRS.

Endyen_Ashish
09-01-2016, 19:09
Hi i have trouble connecting the HUD dash for PC on PS4. is anyone able to help me?

I tried Different UDP settings, different ways to start the game and app, wifi is on. Is there anything specific i need to do?
I'm looking forward to using my phone as HUD...

cjorgens79
10-01-2016, 00:51
You can mask out the bits now, bit 3 isn't used currently, same with the extra joypad bits (they will currently be set to 0). As regards mapping, it's the same as the joypad for the normal buttons, then it will be device dependent.

Yeah ive already fixed the code and submitted a patch for iOS to Apple. That will take around 2 weeks to be approved which is why I wanted to find out when the changes you made would be going live in Project Cars to see whether or not I would have to deal with issues prior to my patch being available.

Gassolini
10-01-2016, 13:25
M
M
M2 (m/s preumably)
KPA
Normalised final position in range
RPS
NM
Thanks! :)

With both RPM and RPS representing the same thing, isn't one of the two redundant?


struct sTelemetryData
{
...
u16 sRpm; // 124
...
f32 sEngineSpeed; // 448
};

BTW, I assume sEngineSpeed is in Radians per second - the RPS in mTyreRPS was wrongly stated to be Revolutions in SharedMemory.h (http://forum.wmdportal.com/showthread.php?16123-API-issues-bugs&p=896004&viewfull=1#post896004).

I'd be tempted to use mRpm = sEngineSpeed * (60.0f / (2.0f * PI)) on unpacking. If we could all agree on the usage, sRpm could be reappropriated.

SenorPez
12-01-2016, 20:29
Has anyone noticed that, occasional, the Type 1 packets will truncate the first character from some of the fields? I thought there was a gremlin somewhere in my code that was causing it, but inspection of the raw data showed (I'm pretty sure) that it was in fact being truncated.

For example, after a while, "125cc Shifter Kart" starts being sent as "25cc Shifter Kart". This seems to happen to everything before the participant names.

As for the update to carClass being included in the T1 and T2 packets, thank you so much. I don't suppose there's a way to shoehorn team in there (for Career telemetry)... but even that one change is ace! Thank you so much!

mr_belowski
12-01-2016, 20:46
The missing first character from string data isn't unique to the UDP data - the shared memory data does this from time to time.

I also see incomplete type 1 packets from time to time that only contain like 8 participants

cjorgens79
13-01-2016, 03:55
The missing first character from string data isn't unique to the UDP data - the shared memory data does this from time to time.

I also see incomplete type 1 packets from time to time that only contain like 8 participants

Re the first char missing, yeah i see that in the data too. Its a real pain in the arse, as i need accurate player names. I'm doing a lot of manual calculations of things that i need to link to players. Some players get moved around in the participantinfo struct in multiplayer when people leave the session so you cant rely on the players participantinfo index so i have to use name as its the only unique thing.

@Tim, any chance that name bug can be fixed for the next patch?

Looking at the data it seems that the first char of the name just gets set to a null terminator for some reason, like they are no longer a valid player, yet they are as they have a race position and are within the valid number of players.

cjorgens79
15-01-2016, 06:50
Yeah ive already fixed the code and submitted a patch for iOS to Apple. That will take around 2 weeks to be approved which is why I wanted to find out when the changes you made would be going live in Project Cars to see whether or not I would have to deal with issues prior to my patch being available.

Must be a bit quiet at apple at the moment, update was approved in record time :) so i'm all set for patch 8 now.

oscarolim
15-01-2016, 09:58
Has the first post been updated with the new structure then?

JohnSchoonsBeard
15-01-2016, 11:52
Is there any way people who disconnect or don't finish could remain on the leaderboard and end of race results in order of the number of laps completed? This would be great for leagues and clubs who like to keep a record of all entrants? Presumably there could be an indicator for Disconnected so their data can be identified and ignored by the game and apps for the remainder of the race? I realise this might need an in game change but is there something that could be done by the apps themselves?

cjorgens79
15-01-2016, 12:14
Has the first post been updated with the new structure then?

no, it was more about me previously accidently reading 4 bits instead of 3 for sector number, it isnt a problem with the current versions but as of patch 8 it would have caused me issues. So ive patch my app so when v8 comes out the sector numbers wont break.

cjorgens79
16-01-2016, 06:28
@Tim - A couple of bugs with the UDP stuff ive come across in the last couple of days

1. When starting a new prac/qual session (eg quick race->start), no UDP data is output even though the session is underway until you leave the garage for the first time. This is problematic if your app monitors the other participants (such as my new live timing screen which monitors each sector times from the other participants)

2. The lap distance field for each participant seems a bit screwy. For example i join a MP session part way through, most of the players are out on track yet lap distance for those players just says 0. It eventually changes, but can sometimes take a fair while. I was hoping to be able to use lapDist to detect when a player was in the pits, sometimes it seems to get set to 0 when they are in the pits, and sometimes not. It would be nice to have some consistency with this field, ie its always > 0 when a player is out on track and its always 0 when a player is in the garage. I think it may also be getting set to 0 sometimes when a lap is invalidated, but im not 100% sure thats always the case.

Biddrace
16-01-2016, 17:09
when will the update android ?

jamesremuscat
17-01-2016, 00:43
Is there any way people who disconnect or don't finish could remain on the leaderboard and end of race results in order of the number of laps completed? This would be great for leagues and clubs who like to keep a record of all entrants? Presumably there could be an indicator for Disconnected so their data can be identified and ignored by the game and apps for the remainder of the race? I realise this might need an in game change but is there something that could be done by the apps themselves?

I haven't spent hours thinking about this, but it seems that this ought to be possible with the data currently available, with a little gymnastics on the app authors' part(s). (The important things would be for them to be keeping their own state, not relying on the data from just the most recent packet; and to be maintaining a map of participant number to participant name, and storing data against the latter not the former. Just a very quick sketch outline from someone who's read the docs but not done masses of coding against them yet!)

SenorPez
17-01-2016, 16:18
Can someone correct me on this if I'm wrong? It seems to me that the only participant that gets their "pit state" (PIT_MODE_DRIVING_INTO_PITS, PIT_MODE_IN_PIT, PIT_MODE_DRIVING_OUT_OF_PITS, PIT_MODE_IN_GARAGE) is the player (where the telemetry is being captured).

Is CPU or other driver Pit state hidden somewhere else, and I'm completely missing it?
Do others have a good way to detect pitting activities otherwise?

cjorgens79
18-01-2016, 09:36
Can someone correct me on this if I'm wrong? It seems to me that the only participant that gets their "pit state" (PIT_MODE_DRIVING_INTO_PITS, PIT_MODE_IN_PIT, PIT_MODE_DRIVING_OUT_OF_PITS, PIT_MODE_IN_GARAGE) is the player (where the telemetry is being captured).

Is CPU or other driver Pit state hidden somewhere else, and I'm completely missing it?
Do others have a good way to detect pitting activities otherwise?

correct, only the player's pit state is output in the api.

the other participants I was hoping could be determined by their lapDist = 0 (meaning in pits), but this is not reliable.

the only successfuly way to do this that ive heard of is from mr_belowski who tracks the co-ordinates of the pits for each track so he can determine if the player is in the pits or not based on their x/z position.

vince34750
19-01-2016, 23:52
Hello,

I have done some good progress with the UDP plugin for XSim, including the gamedashboard. I'm just missing two things now to be on par with the existing SHM plugin: where do I get my lap number and race position. I do not care about the 64 players, but just me :)
I couldn't spot this info in this soooooooo long thread. Should be in sParticipantInfo which is part of sParticipantInfoStrings, but at which position ?
Thanks.

SenorPez
20-01-2016, 03:49
Hello,

I have done some good progress with the UDP plugin for XSim, including the gamedashboard. I'm just missing two things now to be on par with the existing SHM plugin: where do I get my lap number and race position. I do not care about the 64 players, but just me :)
I couldn't spot this info in this soooooooo long thread. Should be in sParticipantInfo which is part of sParticipantInfoStrings, but at which position ?
Thanks.

I think you're a bit mistaken. sParticipantInfo isn't part of sParticipantInfoStrings; it's part of sTelemetryData.

(Deleted wrong info here.)

Note that race position is a binary AND with '10000000' (128) that indicates if the position is active. So for the local player (since that has to be active, right?), position would be always above 128. (Technically, position is sRacePosition minus 128, but that's bad practice. The "correct" way would be to perform a reverse AND operation to extract the bits you want: sRacePosition & int('01111111', 2) [Python])

vince34750
20-01-2016, 09:08
Many thanks. I had missed that, at offset 464 in sTelemetryData as you mentionned.
For the position, it is more a AND with 0x7f, as in your python example.
225573
Now, I just need to cleanup the code and make some more tests before releasing it.
Getting telemetry from a PS4 is so sweeet.

Oh, one more thing. I still have the RACE status which seems weird at times. Known issue ?
SHM says :
// (Type#3) RaceState (to be used with 'mRaceState')
enum
{
RACESTATE_INVALID,
RACESTATE_NOT_STARTED,
RACESTATE_RACING,
RACESTATE_FINISHED,
RACESTATE_DISQUALIFIED,
RACESTATE_RETIRED,
RACESTATE_DNF,
//-------------
RACESTATE_MAX
};

I get RACING sometimes but most of the time it is something else.

cjorgens79
20-01-2016, 12:16
As far as I can tell, the local player is always the first position in the participant arrays, so you could just seek to the first instance of sParticipantInfo within each Type 1 (sTelemetryData) packet for that data.


Thats not accurate, the players index is not always 1. In single player/career sessions it is, but not in online multiplayer. You need to look at the sViewedParticipantIndex field in the sTelemetryData struct to work out which index in the sParticipantInfo is the player. Be aware though, that when looking at replays or using spectator mode, that will be the index of the player you are currently viewing.

cjorgens79
20-01-2016, 12:19
Oh, one more thing. I still have the RACE status which seems weird at times. Known issue ?
SHM says :
// (Type#3) RaceState (to be used with 'mRaceState')
enum
{
RACESTATE_INVALID,
RACESTATE_NOT_STARTED,
RACESTATE_RACING,
RACESTATE_FINISHED,
RACESTATE_DISQUALIFIED,
RACESTATE_RETIRED,
RACESTATE_DNF,
//-------------
RACESTATE_MAX
};

I get RACING sometimes but most of the time it is something else.

When you are in prac/qual mode, when you leave the pits the racestate is RACESTATE_NOT_STARTED. It will stay like that until you have completed your outlap and cross the line to commence your first flying lap. In a race, from memory it is RACESTATE_NOT_STARTED until the lights go green. At the end it will switch to finished, however sometimes the api jumps briefly between finished, then invalid, then back to finished.

vince34750
20-01-2016, 12:28
Thanks for highlighting that point.
Now it should work 100% :
sRacePosition = CByte(UDPBytes(464 + 16 * sParticipant + 8) And &H7F)
And with that sParticipant I can find the player name when receiving the sParticipantInfoStrings packet. Sweet.
Thanks again.

cjorgens79
20-01-2016, 13:16
@Tim - dont forget to update the first page now that patch 8 is released :)

SenorPez
20-01-2016, 13:59
Thats not accurate, the players index is not always 1. In single player/career sessions it is, but not in online multiplayer. You need to look at the sViewedParticipantIndex field in the sTelemetryData struct to work out which index in the sParticipantInfo is the player. Be aware though, that when looking at replays or using spectator mode, that will be the index of the player you are currently viewing.

Thanks for that info. My Internet connection is fairly unstable, so I don't do a lot of online multiplayer. I've removed that information from my post, lest I lead someone astray!

mr_belowski
20-01-2016, 21:26
Anyone managed to get any button presses from the PC version of the game? I'm outputting the raw values of the bytes at position 1366 (dPad), 131 (crash state) and 96 & 97 (joyPad) and all of them are always zero (I haven't actually crashed, so maybe the bottom bits of crashState would change).

I've tried an XBox 360 controller and my Logi DPF wheel, pressing all the buttons I can find while outputting the values of these bytes as the game runs (during a race session). I've made sure I configured the wheel and the controller in-game so I can actually drive with them (assigned throttle / brake / steering controls). I'm assuming that this would make them the "focus device" as mentioned in an earlier post, but it doesn't appear to make any difference - these bytes are always zero. Has anyone else managed to get button presses to work in the UDP data on the PC version since patch 8?

Before patch 8, my 360 controller would send button presses

Bealdor
20-01-2016, 21:42
OP updated per Tim Mann's request.

bendoe
21-01-2016, 00:54
Anyone managed to get any button presses from the PC version of the game? I'm outputting the raw values of the bytes at position 1366 (dPad), 131 (crash state) and 96 & 97 (joyPad) and all of them are always zero (I haven't actually crashed, so maybe the bottom bits of crashState would change).
...


I have only a Logitech G27 wheel with a DPad on the shifter.

I made the same experience! I can press all buttons on the wheel, on the shifter/dpad. I've every zero at Byte 1366 / Byte 96&97 / Byte 131 (except I do crash)!

The crashState-Bits does work!

oscarolim
21-01-2016, 09:28
For the new bit mSameClass, is same class as what? The player car?

mr_belowski
21-01-2016, 10:06
yes

satco1066
21-01-2016, 10:42
OP updated per Tim Mann's request.

where. Can't find it

mr_belowski
21-01-2016, 10:46
Think this is the additional bits in the dPad and joyPad bytes.

Anyone been able to test if the PC version is actually sending button presses at all? It looks like this may be broken

Bealdor
21-01-2016, 10:52
where. Can't find it

I edited the following bits (red = old, green = new):


mTelemetryData.sParticipantInfo[i].sSector=((u8)pMemory->mParticipantInfo[i].mCurrentSector)|((u8)x<<6)|((u8)z<<4); // (enum 3 bits)+(x 2 bits, z 2 bits)->u8

updated:

mTelemetryData.sParticipantInfo[i].sSector=((u8)pMemory->mParticipantInfo[i].mCurrentSector)|((u8)x<<6)|((u8)z<<4)|(pMemoryExtras->mSameClass[i]?8:0); // (enum 3 bits)+(x 2 bits, z 2 bits)+(1 bit same class)->u8



mTelemetryData.sJoyPad=(u16)JOYPAD BUTTON MASK
mTelemetryData.sDPad=(u8)JOYPAD DPAD MASK

updated:

mTelemetryData.sJoyPad=(u16)JOYPAD BUTTON MASK 0-15
mTelemetryData.sDPad=(u8)JOYPAD DPAD MASK (button mask 16-19 <<4)
mTelemetryData.sCrashState|=(button mask 20-23 <<4)


added:

Buttons are mapped from the active device to the 16 bits in .sJoyPad, the top 4 bits of sDPad and the top 4 bits of sCrashState, giving a total of 24 buttons.

cjorgens79
21-01-2016, 12:32
I am seeing nothing but 0's for dpad/joypad too with Patch 8 on the PC. I've asked if some of my pCars Dash app users with consoles can let me know if it works on the consoles or not

mr_belowski
21-01-2016, 12:36
I had a couple of reports from PS4 players that it's working, so I guess this is just the PC version

SenorPez
21-01-2016, 13:21
I've recently run into a question with sParticipantInfoStrings that I've so far avoided due to... luck? Not sure. Maybe introduced in 8.0?

The structure is defined as:


struct sParticipantInfoStrings
{
static const u32 sPacketSize = 1347;
u16 sBuildVersionNumber; // 0
u8 sPacketType; // 2
char sCarName[64]; // 3
char sCarClassName[64]; // 131
char sTrackLocation[64]; // 195
char sTrackVariation[64]; // 259
char sName[16][64]; // 323
// 1347
};


Except... something isn't right. I'm seeing garbage at the end of the packet.

2 (u16) + 1 (u8) + 4*64 + 16*64 = 1283. That's not 1347.
That last 64 bits appears to have a mangled version of the next name (sometimes) (in the additional info strings).
Did I miss a note where you're just supposed to ignore the last 64 bits? Why is it there?

Tim Mann
21-01-2016, 13:38
I've recently run into a question with sParticipantInfoStrings that I've so far avoided due to... luck? Not sure. Maybe introduced in 8.0?

The structure is defined as:


struct sParticipantInfoStrings
{
static const u32 sPacketSize = 1347;
u16 sBuildVersionNumber; // 0
u8 sPacketType; // 2
char sCarName[64]; // 3
char sCarClassName[64]; // 131
char sTrackLocation[64]; // 195
char sTrackVariation[64]; // 259
char sName[16][64]; // 323
// 1347
};


Except... something isn't right. I'm seeing garbage at the end of the packet.

2 (u16) + 1 (u8) + 4*64 + 16*64 = 1283. That's not 1347.
That last 64 bits appears to have a mangled version of the next name (sometimes) (in the additional info strings).
Did I miss a note where you're just supposed to ignore the last 64 bits? Why is it there?

I'm not sure anyone else has spotted that:) There used to be an extra string in there which got removed, hence the extra 64 byte packet size. You can safely ignore the last 64 bytes, they will just contain garbage, I'm not sure changing the packet size now would be a popular decision. I will however blank out the last 64 bytes.

Tim Mann
21-01-2016, 13:43
Alternatively I could stick something else in the 64 bytes, remember it doesn't get sent as often as packet type 0, suggestions on a postcard......

mr_belowski
21-01-2016, 13:45
Ooo, 64 additional bytes of awesome, where to start ;)

Weather forecast data would be my preference if that data's accessible (these packets are sent every 10 seconds aren't they?)

cjorgens79
21-01-2016, 13:47
I've recently run into a question with sParticipantInfoStrings that I've so far avoided due to... luck? Not sure. Maybe introduced in 8.0?

The structure is defined as:


struct sParticipantInfoStrings
{
static const u32 sPacketSize = 1347;
u16 sBuildVersionNumber; // 0
u8 sPacketType; // 2
char sCarName[64]; // 3
char sCarClassName[64]; // 131
char sTrackLocation[64]; // 195
char sTrackVariation[64]; // 259
char sName[16][64]; // 323
// 1347
};


Except... something isn't right. I'm seeing garbage at the end of the packet.

2 (u16) + 1 (u8) + 4*64 + 16*64 = 1283. That's not 1347.
That last 64 bits appears to have a mangled version of the next name (sometimes) (in the additional info strings).
Did I miss a note where you're just supposed to ignore the last 64 bits? Why is it there?

You can see the comments for byte positions are wrong, 3+64 does not equal 131. its out by 64 bytes, if your working from those comments that would be your issue.

cjorgens79
21-01-2016, 13:47
You can see the comments for byte positions are wrong, 3+64 does not equal 131. its out by 64 bytes, if your working from those comments that would be your issue.

ninja'd!

cjorgens79
21-01-2016, 13:50
Ooo, 64 additional bytes of awesome, where to start ;)

Weather forecast data would be my preference if that data's accessible (these packets are sent every 10 seconds aren't they?)

i dont think they are getting sent every 10 seconds, i have to look at the raw data to confirm but i noticed i was losing names when returning to garage mid session, then leaving pits again. Didnt get names again until i crossed the start finish line for the next lap. Havent looked into it properly yet, so it could be names are just blank after returning to garage, packets arent being sent every 10, or i have a bug on my end. Ill check it out in more detail tomorrow and report back.

cjorgens79
21-01-2016, 13:53
Alternatively I could stick something else in the 64 bytes, remember it doesn't get sent as often as packet type 0, suggestions on a postcard......

selfishly i would say

float sFastestLapTimes[16] -- the fastest lap times set in the session for the first 16 players, would solve my f1 live timing issue when joining mid way through a session as i could find out the fastest laps of the players who have already set times

also yes i know this is flawed unless it also occured on the other strings packet too.

mr_belowski
21-01-2016, 13:55
Might be a misunderstanding on my part - I thought the string packets were now (since patch 8) sent at least every 10 seconds

cjorgens79
21-01-2016, 14:00
Might be a misunderstanding on my part - I thought the string packets were now (since patch 8) sent at least every 10 seconds

yeah sorry i wasnt very clear, that is supposed to be the case, i just observed tonight though that it might not be. Ill confirm properly tomorrow

Tim Mann
21-01-2016, 15:10
As regards the NULL in strings, it's caused by the shared mem api and one of the updates occurring at the same time, I'm not going to start putting mutex's in as that would have all sorts of repercussions. I'd suggest keeping a cached copy of the strings, when the packet comes in compare the strings against the cached copy, if it doesn't match then copy it to the cached version, if it does then you know it's valid and and then copy the string to destination. I could do this API side, what is the frequency of this occurring? I've never seen it happen from PS4.

could_do_better
21-01-2016, 16:50
Is a Shared memory option even needed any more? Its totally PC specific whereas UDP is universal.

Mahjik
21-01-2016, 17:48
Is a Shared memory option even needed any more? Its totally PC specific whereas UDP is universal.

There are those who only run on PC and Shared Memory is more efficient when you aren't running external devices. IMO, it's still needed.

flynny75
21-01-2016, 19:37
Great. Push the update before documenting the structure changes, and don't test it works on PCs.
:(

oscarolim
21-01-2016, 20:10
Great. Push the update before documenting the structure changes, and don't test it works on PCs.
:(

On the 8th of January... http://forum.projectcarsgame.com/showthread.php?40113-HowTo-Companion-App-UDP-Streaming&p=1208080&viewfull=1#post1208080

flynny75
21-01-2016, 20:23
On the 8th of January... http://forum.projectcarsgame.com/showthread.php?40113-HowTo-Companion-App-UDP-Streaming&p=1208080&viewfull=1#post1208080

Gah, guess I missed that then. OP wasnt updated until yesterday.

cjorgens79
22-01-2016, 05:27
As regards the NULL in strings, it's caused by the shared mem api and one of the updates occurring at the same time, I'm not going to start putting mutex's in as that would have all sorts of repercussions. I'd suggest keeping a cached copy of the strings, when the packet comes in compare the strings against the cached copy, if it doesn't match then copy it to the cached version, if it does then you know it's valid and and then copy the string to destination. I could do this API side, what is the frequency of this occurring? I've never seen it happen from PS4.

Once the name gets nulled out, it stays nulled out in the api. Even changing sessions doesnt fix it. I have a udp recording of a MP session i did at monza, the players name was fine when i started then part way through qualy the first char got nulled. It stayed that way all through qualy, and even once the race started that players first char was still null. I have another recording from monza months later where there was another player in the list with the blank name as well.

I have made a total of 6 recordings, 1 at brands indy (single player with 29 ai), 2 MP sessions are hockenheim, 2 MP sessions at monza and one MP session at SPA. Out of those recordings, both Monza ones exhibit this issue, none of the others do. It is probably just co-incidence that both occured at monza..?

I wonder if we are being mislead and the first character of the name is null not because its corrupted, but because the actual player in that position has a name that convert to UTF8 or whatever encoding your using in the UDP stream? In the first example where i said a player's name was fine at the start of the session, then became broken part way through and stayed like that through the rest of qual/race, i wonder if what actually happened is that the player left the session and another player joined in their participantInfo slot who's name wasn't able to be stored in the shared memory struct, so it was just being output as null. When i look at the data i see the remainder of the name of the original player, but if they actually left the session then that would be expected. The new player who took over their spot's name should have overwritten theirs but is only null for some reason. Maybe someone with funky characters in their name causes it to come out as null when put into the api. Just a thought.

cjorgens79
22-01-2016, 05:40
Continuing from the above, i just remembered that i recorded video of one of the sessions too so i could compare api outputs vs what was being shown in the game

This is a capture of the first strings packet i received when joining the MP session, matched to the in game leaderboard. This shows my theory in the previous post to be wrong, as the player is clearly Beast** and in the api the first char is null and the rest is there, although you can see that in game that player has ** after there name, in the api its an A followed by a NULL. So some sort of conversion issue perhaps? That players name remains broken through all of qual and the following race session. If you want a copy of the recorded udp data let me know.

225724

Tim Mann
22-01-2016, 10:48
Your theory is probably correct, in that the conversion fails, so just gets replaced with a NULL at the beginning.

Tim Mann
22-01-2016, 13:18
I've changed the string encode from the wcstombs to the same one which is used internally for display names (which is basically a unichar to utf8), I think the original conversion can fail with certain chars (it was the only place in code this function was used). James has fixed the PC button issue.

mr_belowski
22-01-2016, 13:19
Awesome. So does that mean the UDP Strings are now sent as UTF-8 encoded bytes, instead of just using the platform default?

cjorgens79
22-01-2016, 13:31
I've changed the string encode from the wcstombs to the same one which is used internally for display names (which is basically a unichar to utf8), I think the original conversion can fail with certain chars (it was the only place in code this function was used). James has fixed the PC button issue.

Great :) Will we be getting a minor 8.1 patch with this change and the button fix for the PC version? It would be good to get it on PC earlier to give us a chance to make sure all is working properly before the next console patch.

Tim Mann
22-01-2016, 13:39
Awesome. So does that mean the UDP Strings are now sent as UTF-8 encoded bytes, instead of just using the platform default?

Indeed, although in testing I came across a name 'Andreas something' that had an accented char, that came out the same from both functions. But I have a feeling that if a char is found which isn't in the current locale (Eg. you're in MP and have UK locale and other player has Catalan) it may fail the conversion.

Actually, I've just proved it:

u16 pTest[]={0x1ee9, 0x00};
size_t converted;
wcstombs_s( &converted, dest, STRING_LENGTH_MAX, (const wchar_t*)pTest, wcslen((const wchar_t*)pTest) );

This is a Vietnamese character, the above works fine on PS4, but on PC it fails. I've replaced this with an internal Unicode conversion routine and it correctly outputs 0xe1bba900

mr_belowski
22-01-2016, 13:46
OK. This might be a little disruptive to app developers if these Strings are now always UTF-8. I prefer this approach, but we have no (automatic) way of knowing whether our apps are receiving data from the PC, XBox or PS4 version of the game. If this patch his the PC version first then we'll have to decode Strings using different encodings - the current app implementations (which, in my case, assume iso-8859-1 encoding) would make a mess of non-ascii characters.

You said there were some spare bytes at the end of the strings packet - could we have something in there which told the client app what encoding was used?

Tim Mann
22-01-2016, 13:55
The above function (wcstombs) returned the same results as the new one for all the strings I threw at it apart from the Vietnamese one. If you want to quickly provide me with a w_char string I'll run it through it, but I've really run out of time on this now, if it's going to cause issues I'll just revert it.

mr_belowski
22-01-2016, 14:08
as we're talking Steam names and XBox Live IDs here, it's a pretty big set of possible characters.

What happens when you run stuff like:

ŠŒŽšžŸ™

through it?

Tim Mann
22-01-2016, 14:11
Incidentally PS4 has always outputted UTF8 (due to the way the MB conversion defaults). Whats the hex values for those?

mr_belowski
22-01-2016, 14:15
8A, 140 142, 154, 158, 159, 153

I didn't realise the PS4 was already using UTF-8. Does the Xbox? The PC version doesn't (at least on my box :))

cjorgens79
22-01-2016, 14:35
In the example i posted earlier with the screenshot and hex dump, the user according to the in game screenshot was Beast** that was getting nulled out. It didnt look unusual, so i wouldnt have thought that conversion would have anything to do with it. Also why would the ** at the end become an "A" in the api.. unless the game displays ** for chars it cant display properly on the PC and that name does actually contain non english chars in reality and hence is a conversion issue after all..

Tim Mann
22-01-2016, 14:43
u16 pTest[]={0x8A, 0x140, 0x142, 0x154, 0x158, 0x159, 0x153, 0x00};

PC Original:

Fails

New:
c2 8a c5 80 c5 82 c5 94 c5 98 c5 99 c5 93 00


basically the above just fails with the existing conversion.

PS4:

(Both give a utf8 result)

c2 8a c5 80 c5 82 c5 94 c5 98 c5 99 c5 93 00

XB1:

Original just fails.

So in conclusion PC/XB1 would fail if a char was not in the current locale and PS4 would always output utf8 anyway. Therefore I suggest just to go with the new utf8 conversion which is consistent across all locales and platforms.

mr_belowski
22-01-2016, 16:08
Sorry for the delay, kids party stuff to sort out :).

My preference would be to increment the api version number and use utf-8 from now on

Tim Mann
22-01-2016, 16:11
This is going to cause issues with QA and versioning and stuff, so there will just be a fix for PC joypad, everything else is going to remain the same.

mr_belowski
22-01-2016, 16:15
Fair enough, can we keep it on the list for a future update? I know its not exactly core functionality but have a consistent string encoding will make life easier for app developers

Tim Mann
22-01-2016, 16:27
Fair enough, can we keep it on the list for a future update? I know its not exactly core functionality but have a consistent string encoding will make life easier for app developers

It's something really that should have been done in the shared memory api, long long ago (as you said it makes it all nicely consistent). Changing it now will probably break legacy apps and if I up the version number then that'll break a few more.

It's time to call it a day on the changes, tweaks and additions.

I've fixed the dodgy data at the end of that strings struct, although cjorgens79 may want to have a poke around, you never know what you may find.

SenorPez
22-01-2016, 16:56
It's something really that should have been done in the shared memory api, long long ago (as you said it makes it all nicely consistent). Changing it now will probably break legacy apps and if I up the version number then that'll break a few more.

It's time to call it a day on the changes and tweaks.

I've fixed the dodgy data at the end of that strings struct, although cjorgens79 may want to have a poke around, you never know what you may find.

I understand the need to call it a day... as someone who's done a fair bit of QA testing, I can understand--even if it's someone troublesome to have encodings all over the place--the ripple effect that change might have. Thank you any way for your communication and effort.

And for that strings struct hint. ;)

So just to confirm, the encodings being spit out by each platform are:

PS4: UTF-8
XBONE: ??? <-- What is the encoding? Is it UTF-8?
PC: Local machine encoding, so, possibly... anything?


I just want to make sure I'm error-tolerant enough on the encodings.

Thanks!

Tim Mann
22-01-2016, 17:01
I understand the need to call it a day... as someone who's done a fair bit of QA testing, I can understand--even if it's someone troublesome to have encodings all over the place--the ripple effect that change might have. Thank you any way for your communication and effort.

And for that strings struct hint. ;)

So just to confirm, the encodings being spit out by each platform are:

PS4: UTF-8
XBONE: ??? <-- What is the encoding? Is it UTF-8?
PC: Local machine encoding, so, possibly... anything?


I just want to make sure I'm error-tolerant enough on the encodings.

Thanks!

PS4 seems to be very tolerant and just pumps out Utf8 (this was proved by the Vietnamese test), XB/PC will fail with that string. Otherwise PC/XB will use the current 'locale' and convert to multi-byte, the actual function used is 'wcstombs_s'.

mr_belowski
22-01-2016, 17:05
Yeah, I've been in software long enough to know that just because a change is a small amount of dev work that doesn't necessarily translate to a small amount of test and release work.

You definitely sure its not feasible to use those 64 spare (junk) bytes to pass the encoding string? (I gotta ask...)

Tim Mann
22-01-2016, 17:13
Yeah, I've been in software long enough to know that just because a change is a small amount of dev work that doesn't necessarily translate to a small amount of test and release work.

You definitely sure its not feasible to use those 64 spare (junk) bytes to pass the encoding string? (I gotta ask...)

I think you could get into a horrible situation where you put a flag in to say the encoding is X and some old app is expecting Y. There's also the case of splitting UDP into it's own set of strings, which I can see going horribly wrong. You could null out the first char on failure, but I can imagine that may break apps as well. As every one has already dealt with it in some way or another (even with various hacks) it's probably best left.

It may be junk, but junk can often be useful...

mr_belowski
22-01-2016, 17:19
I mean, rather than changing the encoding or the way you encode, if your code uses the default platform encoding (which it does, right?) can you just get the name of that default and bung it in those bytes at the end if the strings packet?

This won't affect old apps at all as nothing has changed, but of course your test and release crew might not be so keen ;)

Tim Mann
22-01-2016, 17:29
I'm going to have to say no (and there's some very non-obvious reasons why).

mr_belowski
22-01-2016, 17:31
Fair enough

oscarolim
22-01-2016, 17:43
I started reading on page 110, was happy, now not so much :p But is understandable, changing a seemingly small thing can have big consequences.

I disagree however that changing the api version number would break existing apps. If it does, then is the developers fault, not SMS. Unless I'm missing the point of the version number, I assumed is so that we know the data comes in a certain way. If that number is different, then the data could potentially be in a different way. If a developer doesn't account for this then... it should.

Anyway, if the encoding can't make it to this game, maybe then for pCars 2 use UTF8 for whatever strings are sent through the API. That would guarantee consistency.

EDIT: Some posts ago, regarding DRS, you mentioned to have a look at the wings[]. At the time, activating the DRS did not change the data received on those bits. Has that change with the current patch?

Chin
22-01-2016, 18:37
Is a Shared memory option even needed any more? Its totally PC specific whereas UDP is universal.

I am unable to utilize UDP...it is a laggy mess for me. :mad: So, no, do not remove shared memory.

BulletEyeDK
22-01-2016, 21:05
@Timm Mann

Will we see further improvements to the UDP feature ?

If so, any chance some of the shared memory features regarding Type #5 Flag Colours, Type #6 Flag Reason, Type #7 Pit Mode, Type#11 Terrain Materials, Type #12 Crash Damage State and the mLastOpponentCollisionMagnitude, mLastOpponentCollisionIndex will ever be featured in UDP ?

I don't know if this is even possible, but if these data was available would it not be possible to somehow use theese data league wise for calculating safety ratings etc. as we know from iRacing ?

When driving in leagues I could easily see this as a much appreciated feature for UDP data...

cjorgens79
23-01-2016, 01:38
I've fixed the dodgy data at the end of that strings struct, although cjorgens79 may want to have a poke around, you never know what you may find.

Thanks, ill keep an eye out for (what i assume is laptime) goodies in that data :)

On a semi related note, not directed at you at all, but im hoping you can pass this on to the right person, im pretty disappointed that the main website app page still has not been updated to reflect support for the UDP api with my app, and number of other peoples. Rob Prange started this thread (http://forum.projectcarsgame.com/showthread.php?43125-UDP-Compatible-Apps-List-Yours-Now!) back on the 17th December, the first two posters who replied on that same day (for crew chief and vr hive), got their apps updated, and everyone else since (5 weeks later) has been ignored. We put alot of time and effort into developing these apps, SMS makes a big announcement about consoles having companion apps, yet anyone that goes to your own website apps page, will only see those two apps as being compatible when there are a fair few more.

pock1910
23-01-2016, 08:54
Is a Shared memory option even needed any more? Its totally PC specific whereas UDP is universal.

Some records are much more accurate with Shared memory than with UDP. For example, some records from world position data at Silverstone National:

UDP:
11.25;-6.5;14.75;-6.5
14.75;-6.5;15.75;-6
15.75;-6;17.25;-6.25
17.25;-6.25;19.25;-6.25
19.25;-6.25;21.75;-6.75
21.75;-6.75;23.25;-7.5
23.25;-7.5;25.25;-7.5
25.25;-7.5;27.75;-7
27.75;-7;30.25;-7.25
30.25;-7.25;30.25;-7.75
30.25;-7.75;33.75;-7.75
33.75;-7.75;34.75;-7.75
34.75;-7.75;37.25;-8
37.25;-8;39.75;-8
39.75;-8;41.75;-8
41.75;-8;43.25;-8.75
43.25;-8.75;44.25;-8.25
44.25;-8.25;46.75;-8.25
46.75;-8.25;48.25;-9.5
48.25;-9.5;50.25;-9

Shared Memory:
11.3019676208496;-6.41540908813477;12.9264965057373;-6.56010103225708
12.9264965057373;-6.56010103225708;15.2636995315552;-6.76650094985962
15.2636995315552;-6.76650094985962;16.8909893035889;-6.90889549255371
16.8909893035889;-6.90889549255371;18.3155651092529;-7.03268480300903
18.3155651092529;-7.03268480300903;20.7596797943115;-7.24306392669678
20.7596797943115;-7.24306392669678;22.2883853912354;-7.37344598770142
22.2883853912354;-7.37344598770142;24.7359466552734;-7.58037948608398
24.7359466552734;-7.58037948608398;26.1649551391602;-7.70025873184204
26.1649551391602;-7.70025873184204;28.6163425445557;-7.90435934066772
28.6163425445557;-7.90435934066772;30.1495532989502;-8.03131675720215
30.1495532989502;-8.03131675720215;31.68359375;-8.15792179107666
31.68359375;-8.15792179107666;34.1395797729492;-8.35992813110352
34.1395797729492;-8.35992813110352;36.3932876586914;-8.54459285736084
36.3932876586914;-8.54459285736084;37.9309425354004;-8.67022514343262
37.9309425354004;-8.67022514343262;39.6746673583984;-8.81210041046143
39.6746673583984;-8.81210041046143;41.933032989502;-8.99494361877441
41.933032989502;-8.99494361877441;43.4735221862793;-9.11912250518799
43.4735221862793;-9.11912250518799;45.8372688293457;-9.30878257751465
45.8372688293457;-9.30878257751465;47.3799171447754;-9.43179988861084
47.3799171447754;-9.43179988861084;49.849853515625;-9.62758541107178
49.849853515625;-9.62758541107178;51.3948249816895;-9.74932098388672

cjorgens79
23-01-2016, 12:17
This is going to cause issues with QA and versioning and stuff, so there will just be a fix for PC joypad, everything else is going to remain the same.

Can just the UDP api be changed to UTF-8 for all platforms and leave shared memory as is? Anyone who interfaces to UDP shouldnt break as they are already dealing with the different encodings from the 3 different platforms. Then you dont need to increment api version and the hassles that can create for some people etc

jimmyb_84
23-01-2016, 22:21
I've made a small amount of progress regarding my eventual plan to create a "strategy app"

I will be creating it for iOS (sorry android) I managed to get most of the socket open/receive/send bits I've also experimented with a few calculations with set figures.

my next challenge is creating the app itself (scary) with the help of Google and Xcode.

1. how do I structure the app i.e files does sent / receive/open go in there own file with the app the "goes to" for instructions.
2. where will anything received go? (I'm a newb)
3. could someone post a code example of extracting one piece of data (can be anything) just trying to get an idea of how the structure for the code works.

any help is great fully received

mr_belowski
23-01-2016, 22:44
1: this is up to you and if you're just starting out, don't spend too much time worrying about it. Start with a simple example app which has one or two UI screens and the basic wiring for a button or two, then make your own simple class (which will be its own source file) which you can, in the UI code, instantiate and query. Once that simple wiring is working things should start making more sense. The IDE should make this kind of stuff easy(ish...)

2: You're receiving raw binary data. Your app is creating race state information from this - presumably you'll have some value objects (classes which hold the state information you create from the binary data). Do you want to save this to the file system or just keep it floating around in memory for the duration of the session?

3: My C# app is open source in my mr_belowski git hub repository. Have a browse through the source (it's a different language but will have fairly similar syntax and semantics). The PCarsUDPDataReader class gets the bytes from the socket then dumps them onto a C# struct. If you have access to a similar thing (struct) in iOS then lucky you. If not you'll need to unpack the data one byte at a time in a big loop I'm happy send a Java snippet which does (some of) this from my Android app. Again, different language but similar enough to be useful.


This will be a steep learning curve but don't sweat it and don't rush it. Stackoverflow.com is every developer's dirty little secret - it's got *loads* of helpful stuff on there. When you're stuck, ask Google with site:stackoverflow.com at the end of the question (e.g. byte array to struct iOS site:stackoverflow.com). Of course, I never do stuff like this ;).

jimmyb_84
24-01-2016, 08:44
1: this is up to you and if you're just starting out, don't spend too much time worrying about it. Start with a simple example app which has one or two UI screens and the basic wiring for a button or two, then make your own simple class (which will be its own source file) which you can, in the UI code, instantiate and query. Once that simple wiring is working things should start making more sense. The IDE should make this kind of stuff easy(ish...)

2: You're receiving raw binary data. Your app is creating race state information from this - presumably you'll have some value objects (classes which hold the state information you create from the binary data). Do you want to save this to the file system or just keep it floating around in memory for the duration of the session?

3: My C# app is open source in my mr_belowski git hub repository. Have a browse through the source (it's a different language but will have fairly similar syntax and semantics). The PCarsUDPDataReader class gets the bytes from the socket then dumps them onto a C# struct. If you have access to a similar thing (struct) in iOS then lucky you. If not you'll need to unpack the data one byte at a time in a big loop I'm happy send a Java snippet which does (some of) this from my Android app. Again, different language but similar enough to be useful.


This will be a steep learning curve but don't sweat it and don't rush it. Stackoverflow.com is every developer's dirty little secret - it's got *loads* of helpful stuff on there. When you're stuck, ask Google with site:stackoverflow.com at the end of the question (e.g. byte array to struct iOS site:stackoverflow.com). Of course, I never do stuff like this ;).

Thank you for the reply, I plan on extracting a few parameters
-fuel rate
-tank size
-race laps/length
-lap time

I can calculate quite a lot from just those, may add tyre wear but getting ahead of my self again. I'm hoping to extract/update using UDP at the end of every lap and recalculate strategy, no point re calculating every 10secs as it'll too much info for someone to take in whilst driving. Regarding the code for this would it be best to discard all UDP traffic and receive packets once a lap.

Thank you access to your GitHub I've bookmarked the page and will take a look in more depth soon. I suppose my next job is to draw up a plan of my basic app and create it (easier said than done).

If I actually get anything working I'll be surprised but will try anyway

Davey-Gravy
24-01-2016, 10:44
I agree entirely with mr_belowski, Stack Overflow will be your friend!

Jimmyb_84 which language are you using on iOS, Objective C or Swift? Depending on which I may be able to help out as I code on iOS.

mr_belowski
24-01-2016, 10:53
Another couple of points to remember -

The data in the UDP / shared memory can be a little noisy. Not sure if fuel data is noisy or not, but don't be too surprised if it bounces around a bit, e.g. if you use a bit of logic like

boolean hasBeenRefuelled = currentFuelLevel > previousFuelLevel
be prepared for some false positives

The fuel level is 0 - 255 in UDP, where 255 is 'full tank' (i.e. it's relative to tank capacity)

Fuel usage per lap varies quite a lot depending on how you drove the lap (if you were stuck in traffic, etc). Your estimated range calculation will need to account for this.

As far as getting the data are concerned, you need to capture all the UDP packets the game sends just so you can spot that packet where the lap count increases. This means that, for every packet your app receives, it'll need to go find the player's lap count data (which is in the ParticipantData array, and the position in that array is indicated by the ViewedParticipantIndex) just so it can tell if the player has crossed the line.

How are you going to present the information to the player? Bear in mind that many players will already have a dash application open and won't be able to move to a different application. It might even be worth considering using audio samples to tell the player. This won't conflict with other apps playing audio data (if Crew Chief ever gets an iOS port)

jimmyb_84
24-01-2016, 11:37
Another couple of points to remember -

The data in the UDP / shared memory can be a little noisy. Not sure if fuel data is noisy or not, but don't be too surprised if it bounces around a bit, e.g. if you use a bit of logic like

boolean hasBeenRefuelled = currentFuelLevel > previousFuelLevel
be prepared for some false positives

The fuel level is 0 - 255 in UDP, where 255 is 'full tank' (i.e. it's relative to tank capacity)

Fuel usage per lap varies quite a lot depending on how you drove the lap (if you were stuck in traffic, etc). Your estimated range calculation will need to account for this.

As far as getting the data are concerned, you need to capture all the UDP packets the game sends just so you can spot that packet where the lap count increases. This means that, for every packet your app receives, it'll need to go find the player's lap count data (which is in the ParticipantData array, and the position in that array is indicated by the ViewedParticipantIndex) just so it can tell if the player has crossed the line.

How are you going to present the information to the player? Bear in mind that many players will already have a dash application open and won't be able to move to a different application. It might even be worth considering using audio samples to tell the player. This won't conflict with other apps playing audio data (if Crew Chief ever gets an iOS port)

Once again thanks for the advice, I have been looking at what UDP label (don't know the actual word) I will search for for current fuel and I couldn't find one so I'm assuming you take current tank level lap1 - tank level lap2 which gives you a number of fuel used?

I'm still learning all this found a few things I'm looking for. I plan to use a 2 page app one for summary showing an overview of planned stops for any race
- stop on lap 5, 9, 13 so on....
- add X amount of fuel per stop

then on page two it'll be live info
-current lap
-last lap
-pit lap X
-add X fuel
then though of a pop up box that says "box box box" when lap = stop on lap

something like that anyway.


I agree entirely with mr_belowski, Stack Overflow will be your friend!

Jimmyb_84 which language are you using on iOS, Objective C or Swift? Depending on which I may be able to help out as I code on iOS.

I will be using swift for my app hopefully, will be on stack overflow a lot.

Tim Mann
24-01-2016, 18:57
Thanks, ill keep an eye out for (what i assume is laptime) goodies in that data :)

On a semi related note, not directed at you at all, but im hoping you can pass this on to the right person, im pretty disappointed that the main website app page still has not been updated to reflect support for the UDP api with my app, and number of other peoples. Rob Prange started this thread (http://forum.projectcarsgame.com/showthread.php?43125-UDP-Compatible-Apps-List-Yours-Now!) back on the 17th December, the first two posters who replied on that same day (for crew chief and vr hive), got their apps updated, and everyone else since (5 weeks later) has been ignored. We put alot of time and effort into developing these apps, SMS makes a big announcement about consoles having companion apps, yet anyone that goes to your own website apps page, will only see those two apps as being compatible when there are a fair few more.

I've poked what should be the correct person to scan that list and update the main site.

Davey-Gravy
24-01-2016, 20:50
I will be using swift for my app hopefully, will be on stack overflow a lot.

Good stuff, Swift is the one to use now if you're starting out. I'm still on Objective C at the moment so I may not be much help to you but feel free to ask.

jimmyb_84
24-01-2016, 22:53
Good stuff, Swift is the one to use now if you're starting out. I'm still on Objective C at the moment so I may not be much help to you but feel free to ask.

I'm the most frustrated person in the world right now (coding is fun!)

I'm attempting a simple on button press multiply x and y and it's not working driving me insane.

Gassolini
24-01-2016, 22:58
Some records are much more accurate with Shared memory than with UDP. For example, some records from world position data at Silverstone National:

...data...
I was hoping some redundant bytes/bits could be used to carry a few more bits of precision for the players car, e.g. in sRpm (redundant now that we have sEngineSpeed). This matters in map making distance calculations etc.. So considering that there will be no more changes, the shared memory interface is still needed for certain things.



As regards the NULL in strings, it's caused by the shared mem api and one of the updates occurring at the same time, I'm not going to start putting mutex's in as that would have all sorts of repercussions....
Does this mean we may still get divided reads using the UDP packets for the other stuff like lap/sector/timing?

oscarolim
25-01-2016, 09:03
In relation to the car RPM, is there a way to get the power band of a car?
My issue is that, I use 12 lights for the RPM value, and I divide the max rpm by 12, giving a linear feel to the lights. However certain cars drop 800-1000rpm only when changing gear, which makes it look like is always on the red line (when it's not).

pjrblue
25-01-2016, 10:53
By default I'm using 11 levels between 75% up to 100%. Below 75% I ignore the data. Don't work for every car, but the result is good enough in my opinion.
The app is going to have an option to adjust the range between 40% up to 100% so every one can adjust for that particular car or preference.

oscarolim
25-01-2016, 11:24
My idea was to test every car (or every car group) and have different settings for each one. Time consuming, but will see.
That 75% seems like a good work around as well. Will have a look into it.

mr_belowski
25-01-2016, 11:25
you'd have to test every car - even in a single car group the cars' engine characteristics vary widely. Just bodge it and get on with the more interesting stuff ;)

vince34750
25-01-2016, 11:37
Hello,

My UDP plugin for XSImEngine is almost done. I just have one strange value for fuel level:

Telemetry DATA received
Game : 2 Session:5
Pos : 1,465833 -40,66538 -0,435147
Accel: 0,8024458 1,075433 -0,5693412
Speed: 32,91657 RPM:7641 Max RPM:8550
Gear: 2
Fuel: 0,08829822 Fuel Capa: 120


As far as I can read in the SHM and UDP spec, there is no mention of multiplier or % for fuellevel. I do get the fuel capacity OK but the fuel level seems very low :o but decreasing appropriately.
Anything I have missed ?
Thanks

mr_belowski
25-01-2016, 11:39
it's a proportion of the tank capacity (255 = full). If the capacity is 120 litres, a fuel value of 127 means you have 60 litres left

vince34750
25-01-2016, 11:53
I have been doing math on an example : Tank of 45 liters. PCars showing 8.3 remaining. I read the value of 0,184 => So the value I read is the percentage in fact.

Davey-Gravy
25-01-2016, 12:13
I thought fuel level in the UDP was a float from 0.0 to 1.0 - multiply 100 to get the fuel level as a percentage.

Or am I confused... It seems to work OK for me like this.

mr_belowski
25-01-2016, 12:15
Sorry, just re-checked my code any you're quite correct, it's a float 0 - 1, not an unsigned byte

Davey-Gravy
25-01-2016, 12:17
Sorry, just re-checked my code any you're quite correct, it's a float 0 - 1, not an unsigned byte

Phew, thought I was going mad then! :D

Davey-Gravy
25-01-2016, 12:19
On a different note - has anyone had any success getting information out of the carflags field? Not a lot happens when I turn things like ABS,TCS etc. on/off - have I missed the point of this field?

Thanks

mr_belowski
25-01-2016, 12:37
No idea mate, I don't use these fields for anything

cjorgens79
25-01-2016, 12:49
On a different note - has anyone had any success getting information out of the carflags field? Not a lot happens when I turn things like ABS,TCS etc. on/off - have I missed the point of this field?

Thanks

Car flags works fine. You wont see anything until you actually "use" those aids. For example, turn on abs then drive at speed onto the grass and slam on the brakes and you should see it change to indicate abs is active. The flags only indicate whether those aids are "doing their thing" at any moment in time, they dont indicate whether or not the aid is actually available on the car.

oscarolim
25-01-2016, 13:10
By default I'm using 11 levels between 75% up to 100%. Below 75% I ignore the data. Don't work for every car, but the result is good enough in my opinion.
The app is going to have an option to adjust the range between 40% up to 100% so every one can adjust for that particular car or preference.

I've got some inspiration on your way, but modified it a bit. For the first 80% I use one third of the lights, and for the remainder 20% I use the remainder 2/3 of lights. Seems to work in an acceptable way.

pjrblue
25-01-2016, 13:43
I've got some inspiration on your way, but modified it a bit. For the first 80% I use one third of the lights, and for the remainder 20% I use the remainder 2/3 of lights. Seems to work in an acceptable way.

I will try this later.

My android app: 4Sim Racing 2nd Screen, is currently on google play in open beta.
If anyone would like to try it and give feedback I appreciated.

link for the beta version: https://play.google.com/apps/testing/com.project.projectcars_secondscreen

video for config file: https://www.youtube.com/watch?v=CYxKiNpAJSY

vince34750
25-01-2016, 14:19
Interesting, but it would be great to have something to know if the aid is on/off, or even better : supported by car and ON, supported by car and OFF, not supported by car and ON, not supported by car and ON (relevant ?)
This could allow to light some LEDS to show the status as the various aids are toggled using keyboard
My 2 cents.

Davey-Gravy
25-01-2016, 14:48
Car flags works fine. You wont see anything until you actually "use" those aids. For example, turn on abs then drive at speed onto the grass and slam on the brakes and you should see it change to indicate abs is active. The flags only indicate whether those aids are "doing their thing" at any moment in time, they dont indicate whether or not the aid is actually available on the car.

Ahh, that'll be it then. I've been sat stationary turning them on and off and getting no readings - makes sense now.

Many thanks!

Davey-Gravy
25-01-2016, 14:51
Interesting, but it would be great to have something to know if the aid is on/off, or even better : supported by car and ON, supported by car and OFF, not supported by car and ON, not supported by car and ON (relevant ?)
This could allow to light some LEDS to show the status as the various aids are toggled using keyboard
My 2 cents.

This is what I wanted to do but misunderstood what the API was sending. Would have been nice to show a status. Nevermind!

vince34750
27-01-2016, 12:37
Hello !

I'm finalizing my Xsim plugin. I did a comparison of my UDP plugin with the current SHM plugin and noticed that the heave/sway/surge (terminology used in motion sims) is different.
Surge is the acceleration of the car in longitudinal direction [g]
Sway is the acceleration of the car in lateral direction [g]
Heave is the acceleration up and down [g]
Two data looked the same: sLocalVelocity and sLocalAcceleration . I expected the sLocalAcceleration to be the one to use, but it looks like sLocalVelocity is more adequate. Could you clarify those two values ? Both are in meters / sec according to the shared memory spec. sLocalAcceleration is not 0 when not moving but is 1g, which seems it includes the standard gravity. Anyone to confirm ?

Here is a video with all values reported:

https://youtu.be/ug0wz7D_NtU

Thanks.

vince34750
28-01-2016, 12:28
I think I figured out the correct order and the correct values to look. Following and advice of a good friend of mine (magicfr) I plotted some results I got from a CSV file I generated using an Audi A1 on a straight line :
226170
Plugin done. :victorious:

cjorgens79
28-01-2016, 13:12
Did anyone make any progress with regards to detecting DRS? Last i remember reading was that the wings value didn't reflect it as expected?

oscarolim
28-01-2016, 13:27
Still the same with the DRS as far as I can tell. It shows the value set on the car setup, but doesn't change if DRS is open or not.

cjorgens79
29-01-2016, 01:44
Still the same with the DRS as far as I can tell. It shows the value set on the car setup, but doesn't change if DRS is open or not.

Yeah agreed, just ran a test myself as well, rear wing read 127 the entire time regardless of whether or not DRS was enabled. Rear wing setting in game was mid way (10 out of 20) on the available range so 127 would make sense. So just seems like DRS doesn't update wing level in the api at all.

oscarolim
29-01-2016, 08:42
Maybe is something they can add in the future. However for a FA, with the rear wing fully open on car setup, the read would be 255 (which is the maximum). Not sure how this could be tackled to avoid any issues with apps that might already use this field for something else.

cjorgens79
30-01-2016, 06:50
So in conclusion PC/XB1 would fail if a char was not in the current locale and PS4 would always output utf8 anyway. Therefore I suggest just to go with the new utf8 conversion which is consistent across all locales and platforms.

@Tim - Sorry to bring this up again, its just that ive spent a number of hours debugging why i was getting occasional crashes on iOS when copying timing results data to the clipboard. I eventually traced it down to the unexpected character encodings in the (PC) data causing the UTF8 conversion from lua strings to iOS NSString to fail resulting in a 3rd party plugin to interface to the iOS pasteboard to cause an unhandled exception.

I know changing the shared memory encoding could potentially cause problems for existing apps, so without changing the shared memory is it not possible to make UTF8 encoding standard for all strings in the UDP stream across all platforms? This wont break any existing UDP compatible apps as all of them will already be working with the UTF8 from the PS4, and different encodings from PC/XB1. So making them all the same as UTF8 would not cause any issues on the app devs end, it will only fix a number of difficulties and issues we are getting from PC / XBox due to the unknown encodings. At the moment the player names with non-english chars (eg there are plenty with latin accents) in my app will only come out correctly when the data comes from the PS4, PC/XB1 users will have malformed names or no name at all.

If its really not feasible to do then so be it, i wont ask again. Im only asking now because we never got clarification on whether or not it was possible to do this just for the UDP stream so the shared memory was not being affected given the concerns re shared memory dependant apps.

Gassolini
31-01-2016, 19:51
When talking about strings...

One update that would be very welcome is always using the same name for mTrackLocation and mTrackVariation. Currently the track location/variation uses strings translated to the language selected in the game. That has created problems for some apps that need to identify the track. This was brought up on WMD a couple of times, where some suggested using a track ID. That is of course out of scope by now, but a non app breaking solution (more of an app fixing solution :)) would be to always use e.g. the English names.

mr_belowski
31-01-2016, 20:08
For track location I fall back to the track length if the name isn't one of the expected English names. Not ideal but does work.

For string encoding I have to ask my users to specify UTF-8 (ps4) or Cp1252 (Xbox/PC)

mr_belowski
31-01-2016, 21:57
anyone notice that PC version is now 8.1? At least, it is according to the little version number text in the bottom left of the main screen. My wheel button presses still don't appear to be in the UDP data tho. I'll have a closer look tomorrow :(

oscarolim
01-02-2016, 09:19
For track location I fall back to the track length if the name isn't one of the expected English names. Not ideal but does work.

For string encoding I have to ask my users to specify UTF-8 (ps4) or Cp1252 (Xbox/PC)

That's gold information :)

cjorgens79
05-02-2016, 04:17
@Tim, in Time Trial mode the ghosts added to the session appear in the participants list but their data doesnt appear to update around the lap. Ie they always show sector 1, always show the same lastSectorTime value and their lapDist doesnt change. There positions on the map however do change. Is this likely to get fixed? I just want to know whether or not i will need to spend time putting in some special code to deal with TT mode to remove the ghosts from the live timing screen and reposition the player to the top of the leaderboard. If its going to be fixed then i will likely leave it as is and just wait for the patch.

Kain NL
15-02-2016, 09:28
Just got an email from Sensadigit...DashMeterPro is being tested on consoles at this moment...and if tests are okay, the app will work on consoles too in a couple of days !!

AtomicSphincter
15-02-2016, 09:35
Just got an email from Sensadigit...DashMeterPro is being tested on consoles at this moment...and if tests are okay, the app will work on consoles too in a couple of days !!

That's great news!

Henrique Clausing
15-02-2016, 11:00
Is there any way to create a WPF / C#;VB.NET like app that overlays the pcars.exe screen? This question is about Live Streaming for a championship, showing standings, for example.

oscarolim
15-02-2016, 12:31
Found this answer in stackoverflow: http://stackoverflow.com/questions/15713943/displaying-an-overlay-above-another-program
The example is in C#.

Henrique Clausing
15-02-2016, 15:12
Thank you so much!

SenorPez
16-02-2016, 15:43
Just to confirm: There are no data feed changes in 9.0, correct?

mr_belowski
16-02-2016, 15:44
Correct, just the fix for button data in the PC version

jamesremuscat
16-02-2016, 15:45
Just to confirm: There are no data feed changes in 9.0, correct?

The release notes (http://forum.projectcarsgame.com/showthread.php?44918-Project-CARS-PC-Patch-9-0-Release-Notes) list the relevant changes.

SPOILER: joypad/steering wheel buttons should now appear in the feed.

EDIT: Ninja'd...

jimmyb_84
21-02-2016, 14:29
Needed to share this, I know it's nothing amazing but this is the first function I've managed to write from scratch in swift (iOS) and it's actually worked. I'm slowly getting the hang of it.

Onwards and upwards

227980

AtomicSphincter
22-02-2016, 08:53
Needed to share this, I know it's nothing amazing but this is the first function I've managed to write from scratch in swift (iOS) and it's actually worked. I'm slowly getting the hang of it.


Great job!
I'm also learning programming from scratch, but I started with Python 3. Here's the code I wrote to get info from the game.


#!/usr/bin/env python3

from socket import *

s=socket(AF_INET,SOCK_DGRAM)
s.bind((' ',5606))
msg=s.recvfrom(2048)
print(msg[0])

mr_belowski
22-02-2016, 09:15
Getting that first foot-hold is such a big step, even if the actual code that is being run is trivial. Well done fella, it gets a lot more fun and a lot more satisfying from this point forward :)

Ivan Dumalovski Janjusic
22-02-2016, 12:29
Correct, just the fix for button data in the PC version
I assume we can't expect this for PS4!'

cjorgens79
22-02-2016, 12:32
I assume we can't expect this for PS4!'

the PS4 and XB1 were never broken, only the PC was

Ivan Dumalovski Janjusic
22-02-2016, 12:36
the PS4 and XB1 were never broken, only the PC was
If I were a women I would kiss you now :-D

jimmyb_84
22-02-2016, 13:19
Getting that first foot-hold is such a big step, even if the actual code that is being run is trivial. Well done fella, it gets a lot more fun and a lot more satisfying from this point forward :)

I seem to take one step forwards and 3 back. I'm understanding some of the concepts but then when it come to writing syntax I completely forget it. I won't give in.... this is going to take a while


Great job!
I'm also learning programming from scratch, but I started with Python 3. Here's the code I wrote to get info from the game.


#!/usr/bin/env python3

from socket import *

s=socket(AF_INET,SOCK_DGRAM)
s.bind((' ',5606))
msg=s.recvfrom(2048)
print(msg[0])



Impressive stuff your advancing further than me look forward to seeing your endeavours

Kain NL
23-02-2016, 10:19
Hey Mr_belowski,

Could you please look into this:

System: PS4
Using: Crew Chief app on HTC M8 recently upgraded to Android V6.0 Marshmallow.

I've been moving various apps to my SD memory card, since internal storage became more full and affected my phones performance.
So I moved the Crew Chief app to my SD card too, and tried using it last Sunday but it didn't work. I got the message radio check' but after that it remained quiet and ddidn't receive any packages.
After moving it back to my phones internal storage it worked fine again!

mr_belowski
23-02-2016, 12:15
did you reboot your phone after moving the app to external storage? I'm just guessing, but it might help.

As most Android devices no longer need this option (and it's not even available on many devices), I've not tested the app in this configuration and have no plans to support this configuration

Kain NL
24-02-2016, 11:26
Nope didn't reboot my phone. Will try it, and Thnx for further info !

mr_belowski
24-02-2016, 11:28
I'm in the middle of adding some optional debug logging to the app, so we might be able to work out what the problem is soon :)

Kain NL
24-02-2016, 19:55
Replaced on my SD card, rebooted my phone and works on my X1, will test also on my PS4..but I think that won't be a problem either. Thnx again!

jimmyb_84
25-02-2016, 10:40
Little update, I'm making a bit of progress it's not ground breaking...

example 1
228246

example 2
228247

next I need to figure out how to control the output value format to 40 not 40.740740740........

then I'll move onto working out a full race distance fuel strategy that includes multiple stops, all inputs will be connected to UDP (hopefully) so will change as the fuel rate changes. Lots of work but i'm heading in the right direction.

mr_belowski
25-02-2016, 10:46
Not being a Swift programmer I don't know if the .0 will be included in the String for a Double with no decimal part - maybe something like
floor(fuelTank / rate)


Maybe
Int(floor(fuelTank / rate))

to get rid of a trailing .0

Or just cast it to an int without rounding (which will truncate the fractional part)
Int(fuelTank / rate)

jimmyb_84
25-02-2016, 11:05
I'm not sure if I can use Int as "rate" is Double by default and "laps" is Int hence why I'd used "Double(laps)" to covert to double otherwise I get an error as cannot * "Int * Double" they must be the same.

I'm thinking (with googles help) that NSString can control format and maybe I need to declare an instance of that to say boxOnLap (add format) then replace the math bit in my print string with that... maybe not tried yet

pjrblue
25-02-2016, 11:08
I'm not sure if I can use Int as "rate" is Double by default and "laps" is Int hence why I'd used "Double(laps)" to covert to double otherwise I get an error as cannot * "Int * Double" they must be the same.

I'm thinking (with googles help) that NSString can control format and maybe I need to declare an instance of that to say boxOnLap (add format) then replace the math bit in my print string with that... maybe not tried yet

Like mr_belowski said, you need to cast.

first example:
print("Start with \(fuelTank)ltrs and Box on lap \(Int(Double(fuelTank) / rate))")
print: "Start with 110ltrs and Box on lap 40\n"

second example:
print("You are good to the end \(Int(raceFuel))ltrs will last")
print: "You are good to the end 13ltrs will last\n"

jimmyb_84
25-02-2016, 11:28
brilliant stuff cheers guys! I managed to get a return (Int) however the math appears to be lost should read 40 but I'm getting a return of 55 ummm...... what going on now

mr_belowski
25-02-2016, 11:30
we need to see your code. And, er, pro-tip... if ever you think the computer is simply wrong, it's *always* you that's done it wrong and you just haven't spotted it yet ;)

jimmyb_84
25-02-2016, 11:33
we need to see your code. And, er, pro-tip... if ever you think the computer is simply wrong, it's *always* you that's done it wrong and you just haven't spotted it yet ;)

Don't worry I know it's me ;)

here is my code:
228252

edit* I know what it's doing somehow it's been rounded down to 2 and is dividing 110 / 2 instead of 110 / 2.7

did a print statement below code to see what value it has

mr_belowski
25-02-2016, 11:40
you're casting the rate (2.7) to an int (2) then dividing 110 by this number (to get 55)

you need to do the division first, then cast the result - Int((Double) fuelTank / rate), or something like that. (Double) fuelTank / rate will give you the number with lots of decimal place. Then the outer Int(...) will cast this number to an int

jimmyb_84
25-02-2016, 11:49
you're casting the rate (2.7) to an int (2) then dividing 110 by this number (to get 55)

you need to do the division first, then cast the result - Int((Double) fuelTank / rate), or something like that. (Double) fuelTank / rate will give you the number with lots of decimal place. Then the outer Int(...) will cast this number to an int

BINGO!!! I could kiss you!

228253

Thank you, onwards and upwards

MisterO
06-03-2016, 09:46
Don't know where to put this so I'll just start here:
I wanted to start fiddling around a bit with the UDP-Data so I activated the UDP streaming in the PCars gameplay options (set it to 4). To see if it's working I tried with the DashMeterPro-App but didn't get anything shown (no gear, rpm whatever) - using shared memory everything works fine. I fired up Wireshark and noticed that I couldn't see anything being send over UDP that looked like it was coming from pcars which seemed a bit odd to me as there should be something.

My profile is the same since launch, system is Windows 10. Anyone else had a similar issue?

tgrey
06-03-2016, 09:49
Don't know where to put this so I'll just start here:
I wanted to start fiddling around a bit with the UDP-Data so I activated the UDP streaming in the PCars gameplay options (set it to 4). To see if it's working I tried with the DashMeterPro-App but didn't get anything shown (no gear, rpm whatever) - using shared memory everything works fine. I fired up Wireshark and noticed that I couldn't see anything being send over UDP that looked like it was coming from pcars which seemed a bit odd to me as there should be something.

My profile is the same since launch, system is Windows 10. Anyone else had a similar issue?

Have you relaunched the game since changing the setting? I seem to remember mine didn't work the first time either.

mr_belowski
06-03-2016, 09:58
The game doesn't send packets when it's minimised or running in the background.

Also, I never got to the bottom of this and can't remember how I spotted it, but if you use vbox on the PC running the game, just having the vbox virtual network adapter installed stops the UDP stuff working. Once I removed that device in device manager i could see the packets in wireshark.

Edit... Perhaps the game sends the UDP data to the first network interface it finds, so if you have multiple network cards (real or virtual) the data might not end up being sent to your lan

jimmyb_84
06-03-2016, 11:39
Ok so I'm doing ok with my learning swift, still get confused easily but powering on....

I've managed to figure out how to work with fuel rates, laps and fuel tank and work out some basic strategy requirements. Next on my list is lap times and tyre wear from this and the above I'm hoping I'll be able to right a function/s that can simulate the remainer of any current race and give you a best/worst case scenario I may also need to figure out track positions for when exiting the pits you'll slot in here...

quick example regarding tyre wear does it output a percentage (out of 100) or just a number per lap. as for lap times do you get hh:mm:ss.000 or just mm:ss.000 all help welcome as I'll be learning to work with lap times shortly and would like a idea of what format I'm going to have to deal with.

My question is what format do the laps times and tyre wear appear from UDP data? or do I set the format when extracting? I'm working out my model before I plum in the UDP socket and such. I cross that bridge later, I'm also thinking I could send some data my app for current apps to receive and visually or via audio inform the player, some feedback on this from current developers would be required as not sure if it's actually possible.

bare in mind this won't be available anytime soon but I'm determined to get something up and running.

oscarolim
06-03-2016, 12:40
Also you shouldn't have both shared memory and udp active. Either one or the other.

Tire wear is a percentage (0 no wear, 1 full wear). For example if the wear is at 31%, it will return 0.31
Times is in milliseconds.

You have to convert the data when extracting. All the UDP sends is bytes, you then have to convert to the appropriate structures (tire wear and times are floats).

mr_belowski
06-03-2016, 12:43
Also you shouldn't have both shared memory and udp active. Either one or the other

That isn't true, at least for me it works fine with them both enabled

jimmyb_84
06-03-2016, 13:57
Also you shouldn't have both shared memory and udp active. Either one or the other.

Tire wear is a percentage (0 no wear, 1 full wear). For example if the wear is at 31%, it will return 0.31
Times is in milliseconds.

You have to convert the data when extracting. All the UDP sends is bytes, you then have to convert to the appropriate structures (tire wear and times are floats).

Thanks for the info! the fact that lap times are considered a float has probably saved me an hour ;)

I've been taking a look at swifts (iOS) library and is has NSStringTimeInterval which is essentially a stop watch :) so I'm thinking I could time laps within the app but knowing when I've crossed the line would depend on the UDP data. Is the positing or lap count data reliable enough for me to try? The other downside to this is I'd have to get my socket up and running so I can test it.

oscarolim
06-03-2016, 15:01
Is better to use the lap time the app sends as a base, otherwise you will get into all sorts of issues that you will need to account for (delay on receiving data, if the player pauses the game, etc).

mr_belowski
06-03-2016, 15:20
For opponent lap times there's no alternative to timing them yourself (using the current sector number in the participant data array as an indicator of when they cross the line), but as oscarolim says, pausing, exiting to the pits, accelerating time etc all wreak havoc with this method.

jimmyb_84
06-03-2016, 15:22
Is better to use the lap time the app sends as a base, otherwise you will get into all sorts of issues that you will need to account for (delay on receiving data, if the player pauses the game, etc).

good point, this programming stuff is rather complicated, who knew....

I'll report back in a year got somethings to try out.

oscarolim
06-03-2016, 16:24
For opponent lap times there's no alternative to timing them yourself (using the current sector number in the participant data array as an indicator of when they cross the line), but as oscarolim says, pausing, exiting to the pits, accelerating time etc all wreak havoc with this method.

No need to time it yourself, you can use the last sector and current sector, keep a record of that, and when the current sector is 2 (new lap started) add the 3 sector times you have previously saved somewhere (checking of course you have 3 sector times to add up to a lap).

MisterO
06-03-2016, 18:23
The game doesn't send packets when it's minimised or running in the background.

Also, I never got to the bottom of this and can't remember how I spotted it, but if you use vbox on the PC running the game, just having the vbox virtual network adapter installed stops the UDP stuff working. Once I removed that device in device manager i could see the packets in wireshark.

Edit... Perhaps the game sends the UDP data to the first network interface it finds, so if you have multiple network cards (real or virtual) the data might not end up being sent to your lan

Hmm...I do have Virtual Box installed...I'll try tomorrow (got sucked up in Assassins Creed Syndicate today ) and see if that helps.

oscarolim
06-03-2016, 22:49
Hi everyone. Could someone shed some light on the meaning of the following variables:
Orientation, LocalVelocity, WorldVelocity, AngularVelocity, LocalAcceleration, WorlAcceleration and ExtendsCentre.
I know they are vectors but I'm unsure what the values relate to.

jimmyb_84
07-03-2016, 16:13
Made real progress last night/this morning with my calculation trials for my eventual app.

figured out if I can get the lap time data into my code in seconds I can calculate all sorts based on lap times, and I even figured out how to then display in a way the user can understand mm:SS.000 I have a couple of format problems to sort I.e when calculations show a single digit "5" for example I need to make it "05" as it looks odd.

Quite pleased with myself but still a while to go and my code is probably a right mess.

Davey-Gravy
07-03-2016, 16:39
Made real progress last night/this morning with my calculation trials for my eventual app.

figured out if I can get the lap time data into my code in seconds I can calculate all sorts based on lap times, and I even figured out how to then display in a way the user can understand mm:SS.000 I have a couple of format problems to sort I.e when calculations show a single digit "5" for example I need to make it "05" as it looks odd.

Quite pleased with myself but still a while to go and my code is probably a right mess.

Sounds like good progress, nice work!

Swift string formatting is the same as ObjC so I can help on this one! In your string formatting command you can use %02i instead of %i to output the integer as 2 digits with a leading zero if necessary. Simples! :D

MisterO
07-03-2016, 18:11
Also, I never got to the bottom of this and can't remember how I spotted it, but if you use vbox on the PC running the game, just having the vbox virtual network adapter installed stops the UDP stuff working. Once I removed that device in device manager i could see the packets in wireshark.

Edit... Perhaps the game sends the UDP data to the first network interface it finds, so if you have multiple network cards (real or virtual) the data might not end up being sent to your lan

After deactivating every network adapter except the one that's actually used to connect to my router it works - I had two virtual box adpaters plus several hyper-v adapters listed in the device manager. I simply shut them all down. I should clean that up someday, didn't get the virtual box host-only network running in Win10 anyway...

Thanks for the help!

jimmyb_84
07-03-2016, 23:07
Sounds like good progress, nice work!

Swift string formatting is the same as ObjC so I can help on this one! In your string formatting command you can use %02i instead of %i to output the integer as 2 digits with a leading zero if necessary. Simples! :D

I know have that sorted now and guess what another problem forgot to consider hours for long races, once I've sorted that I have fuel, tyres and laptime calculations sorted next on the list is to make my program do stuff which includes simulatating a race while you/I race then I'll cross the bridge of importing data and finally rigging it to a UI and testing..... still a long way to go, I'm surprised I got this far to be honest

Davey-Gravy
10-03-2016, 16:13
Just trying to decipher all of the timing info in the UDP data and have a quick question:

I have the list of driver names from the sParticipantInfoStrings/sParticipantInfoStringsAdditional packets and I have the sParticipantInfo from sTelemetryData. However... how do I marry the two sets of data up as they don't seem to line up?

It looks like everything is offset by 1 but is this correct or is that particular to my current test data?

Cheers!
David

cave
11-03-2016, 09:10
@jimmyb_84

I have done it like this:



var currentTime:Float = 0

let time:Int = max(0,Int(currentTime))
let milli = Int(1000 * (currentTime%1))
let seconds = time % 60
let minutes = (time / 60) % 60

let t = String(format: "%02d:%02d.%03d", minutes, seconds, milli)

jimmyb_84
11-03-2016, 09:21
@jimmyb_84

I have done it like this:

var currentTime:Float = 0

let time:Int = max(0,Int(currentTime))
let milli = Int(1000 * (currentTime%1))
let seconds = time % 60
let minutes = (time / 60) % 60

let t = String(format: "%02d:%02d.%03d", minutes, seconds, milli)

Thanks for posting your code, I've done similar but think yours is cleaner than mine, out of interest was is the "%1" on the end of the milli calculation do? not come across it before. Don't t have my code to hand will post snap shot soon.

cave
11-03-2016, 09:28
Thanks for posting your code, I've done similar but think yours is cleaner than mine, out of interest was is the "%1" on the end of the milli calculation do? not come across it before. Don't t have my code to hand will post snap shot soon.

it gives you just the digits after the dot..!

jimmyb_84
11-03-2016, 09:34
it gives you just the digits after the dot..!

Wish I'd of known that sooner spent ages trying to figure that out! Cheers

oscarolim
11-03-2016, 11:09
it gives you just the digits after the dot..!


Wish I'd of known that sooner spent ages trying to figure that out! Cheers

Mathematically speaking, it gives the module (remainder) of a division. Since you're dividing by one, it will give the decimal part.
Another very used one is number%2, which if 0 means number is even and if different than 0 number is odd.

To those that hate math classes: math is important for programming!!! :)

jimmyb_84
11-03-2016, 12:37
here is what i used got lap time,



var lapTime: Float = 80.247 //1:20.247 converted to seconds INPUT
let minutes = Int(lapTime / 60)
let seconds = Int(lapTime - 60)
let lapTimeSeconds1 = Float(lapTime) - Float(minutes * 60)
let lapTimeMillisecondsFormat = String(format: "%.3f", Float(lapTimeSeconds1))

with this print line


print("Lap Time is \(minutes):\(lapTimeMillisecondsFormat)")

i have converted lap time to seconds so i can make time maths easier, then convert it back for display purposes.

cave
11-03-2016, 19:45
here is what i used got lap time,



var lapTime: Float = 80.247 //1:20.247 converted to seconds INPUT
let minutes = Int(lapTime / 60)
let seconds = Int(lapTime - 60)
let lapTimeSeconds1 = Float(lapTime) - Float(minutes * 60)
let lapTimeMillisecondsFormat = String(format: "%.3f", Float(lapTimeSeconds1))

with this print line


print("Lap Time is \(minutes):\(lapTimeMillisecondsFormat)")


i have converted lap time to seconds so i can make time maths easier, then convert it back for display purposes.


Problem is that subtraction will only work in a certain range.

By using the modulo operator (%) it will give you the following results:

60,61,120,121 = 0,1,0,1

jimmyb_84
11-03-2016, 20:45
Good news, I have a very basic version of my app working! It's manual input and doesn't tell you much but it's working!

I need to figure how to hide the keyboard when done.

229648

@cave

I'm probably going to need to build some IF statements regarding when laptime is zero but it "should" work providing it has data

cjorgens79
13-03-2016, 05:26
@Tim - There is a bug in the api output with the timing data for participants. Basically an incorrect sector time is output due to the participant skipping a whole section of track by jumping to the pits and returning to track. For example, i have a recording of this occurring at SPA. A player has just started sector 3 on their flying lap when they did "return to pits". Once in the pits they immediately hit start to drive out of the pit lane, rejoining the track and crossing the start finish line. At no point does the players lap get invalided in the api, its outputting invalid=false before jumping to the pits and after jumping to the pits. So as soon as they cross the line once leaving the pits to start sector 1 the api is outputting a sector time for sector 3 that is "valid" and was the result of jumping to the pits, resulting in vastly faster sector times than are possible.

This is only an issue in the API data, the game itself does not record the lap as valid, but anyone using the API does because the api never indicates the lap was invalidated. So as a result i am seeing incorrect lap times in my timing screens due to this.

Any chance of a fix for the next patch?

jimmyb_84
13-03-2016, 14:38
Just wondering if any of the current iOS app developers have created a socket for UDP using swift? I've had a quick search online and the route for creating this seems a little fuzzy. I've seen socket.io but not sure if that's what I need.

I'm completely new to this a bunt in the right direction would be amazing, also how best to test? shall I create a socket desperate to my current app to test and send data from the game or MikeyTT's simulator app I seem to recall. This is the next stage of my learning and it's a huge leap for me.

I've also had the idea of updating users with las test info using built in notifications but with accessibility turned on you can get Siri to read them, so no glances needed (just a though haven't even begun to explore it)

cave
14-03-2016, 06:02
Just wondering if any of the current iOS app developers have created a socket for UDP using swift? I've had a quick search online and the route for creating this seems a little fuzzy. I've seen socket.io but not sure if that's what I need.

I'm completely new to this a bunt in the right direction would be amazing, also how best to test? shall I create a socket desperate to my current app to test and send data from the game or MikeyTT's simulator app I seem to recall. This is the next stage of my learning and it's a huge leap for me.

I've also had the idea of updating users with las test info using built in notifications but with accessibility turned on you can get Siri to read them, so no glances needed (just a though haven't even begun to explore it)



Check out GCDAsyncUdpSocket. Its in objective-c, but with an bridging-header in your project you can import the objective c files (#import "GCDAsyncUdpSocket.h")




class App: NSObject, GCDAsyncUdpSocketDelegate {

private var udpSocket:GCDAsyncUdpSocket?
private let parser:YourParser()

override init() {

super.init()

createSocketConnection()

}


func createSocketConnection() {

udpSocket = GCDAsyncUdpSocket(delegate: self, delegateQueue: dispatch_get_main_queue())

if let socket = udpSocket {

bindAndBeginReceiving()

//print(socket.localPort())
//print(socket.localHost())

}

}



func bindAndBeginReceiving() {

do {
try udpSocket?.bindToPort(5606)
}

catch _ as NSError {
// error handling
}

do {
try udpSocket?.beginReceiving()
}

catch _ as NSError {
udpSocket?.close()
// error handling
return
}

}

/*
func udpSocket(sock: GCDAsyncUdpSocket!, didConnectToAddress address: NSData!) {
// print("didConnectToAddress")
}

func udpSocket(sock: GCDAsyncUdpSocket!, didNotConnect error: NSError!) {
// print("didNotConnect")
}

func udpSocket(sock: GCDAsyncUdpSocket!, didNotSendDataWithTag tag: Int, dueToError error: NSError!) {
// print("didNotSendDataWithTag")
}

func udpSocket(sock: GCDAsyncUdpSocket!, didSendDataWithTag tag: Int) {
// print("didSendDataWithTag")
}

func udpSocketDidClose(sock: GCDAsyncUdpSocket!, withError error: NSError!) {
// print("udpSocketDidClose")
}
*/

func udpSocket(sock: GCDAsyncUdpSocket!, didReceiveData data: NSData!, fromAddress address: NSData!, withFilterContext filterContext: AnyObject!) {

//print(data)

parser.nextBuffer(data, address: address)

}

}

jimmyb_84
14-03-2016, 12:36
Cheers for the reply, I was hoping to implement something in swift but I'll take what you have kindly shared for now, it'll move me on a bit. I'll see what happens

mr_belowski
14-03-2016, 12:37
Seems weird that Swift doesn't have its own API for dealing with UDP sockets

jimmyb_84
14-03-2016, 12:58
Seems weird that Swift doesn't have its own API for dealing with UDP sockets

If it has one it's well hidden and not documented very well, can find nothing in Apple released documentation, may have to adopt this.

cjorgens79
14-03-2016, 13:08
@Tim - found another bug with the api. When you start a race that is set to be a timed race (eg 5 mins), during the start sequence (grid camera pan) the EventTimeRemaining variable has a crazy value. It is FF FF 7F 7F in the data which is 4,294,934,399. It changes later to be correct once the lights start.

pjrblue
14-03-2016, 14:38
Cheers for the reply, I was hoping to implement something in swift but I'll take what you have kindly shared for now, it'll move me on a bit. I'll see what happens

I found this: https://github.com/swiftsocket/SwiftSocket/tree/master/SwiftSocket

I made a small UDP socket in c# that sends a text message, and with this code it received the info, I made a console app for the test.


import Foundation
import Darwin.C

print("Hello, World!")

var b = false;
var i = 0;

let server:UDPServer=UDPServer(addr:"255.255.255.255",port:11000)
let run:Bool=true
print("server.started")


while (!b)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), { () -> Void in
let (data,remoteip,remoteport)=server.recv(2048)

//prints IP ; Port
//print("receive \(remoteip);\(remoteport)")

if let d=data
{
if let str=String(bytes: d, encoding: NSUTF8StringEncoding)
{
print(str)
}
}
})
}

You need the "yudpsocket.c", "yudpsocket.swift" and "ysocket.swift", after importing the files you need another file "UDP_Test_Console-Bridging-Header" and inside that, the file was created automatically if you add the files manually

//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "yudpsocket.c"

jimmyb_84
14-03-2016, 17:19
Cheers, looks a little complicated for a newbie but I'll see what I can get working, do so let's work in playground on Xcode, just want to practice before I set it lose on my current version.

pjrblue
15-03-2016, 08:31
Cheers, looks a little complicated for a newbie but I'll see what I can get working, do so let's work in playground on Xcode, just want to practice before I set it lose on my current version.

I don't know if the code works in playground, I couldn't get it work there, so I created an OS X Console.

There is some problem, sometimes the UDP read crash, need to implement a try catch.
Also the app is generating a loot of memory usage, it crash after getting about 60GB of memory usage, and I don't know what is causing this huge memory usage.

I tested on X1, I have also the PS4 game I could try it later.

I made some progress reading the data, sometime today I paste the code.

Edit:
Code after new test. The memory huge usage was caused because the sleep was inside the thread and not in the while cycle.




import Foundation
import Darwin.C

var b = false;
var i: Float = 0;

var msgData : NSMutableData! = nil;

let server:UDPServer=UDPServer(addr:"255.255.255.255", port:5606);

let run:Bool=true;

//array for GameState
let gState:[String] = ["EXIT", "FRONT_END", "PLAYING", "PAUSED"];
//array for SessionState
let sState:[String] = ["INVALID", "PRACTICE", "TEST", "QUALIFY", "FORMATION_LAP", "RACE", "TIME_ATTACK"];

print("server.started");
while (!b)
{
i++;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), { () -> Void in

do
{
var (data,remoteip,remoteport)=server.recv(2048);
msgData = NSMutableData(bytes: data!, length: data!.count);
let ptr = UnsafeMutablePointer<UInt8>(msgData.mutableBytes);
var b = UnsafeMutableBufferPointer<UInt8>(start: ptr, count: msgData.length);

//how to get Packet Type
var byte = msgData.subdataWithRange(NSMakeRange(2, sizeof(UInt8)));
var pType: NSInteger = 0;
byte.getBytes(&pType, length: (sizeof(NSInteger)));
var packetType = pType & 0x02; //get packetType (0, 1 or 2)
//print ("packetType\(packetType)");

//how to get GameState & GameSession
byte = msgData.subdataWithRange(NSMakeRange(3, sizeof(UInt8)));
var gSession: NSInteger = 0;
byte.getBytes(&gSession, length: (sizeof(NSInteger)));
var gameState: Int = gSession & 0xF;
var sessionState: Int = gSession >> 4 & 0xF; //if you don't know how this work, ask
//print ("gameState:\(gameState); sessionState:\(sessionState)");

//how to get Number of Participants
byte = msgData.subdataWithRange(NSMakeRange(5, sizeof(UInt8)));
var numParticipants: NSInteger = 0;
byte.getBytes(&numParticipants, length: (sizeof(NSInteger)));
//print("numParticipants: \(numParticipants)");

//how to get Laps in Event
byte = msgData.subdataWithRange(NSMakeRange(11, sizeof(UInt8)));
var lapsInEvent: NSInteger = 0;
byte.getBytes(&lapsInEvent, length: (sizeof(NSInteger)));
//print("lapsInEvent: \(lapsInEvent)");

//how to get Best Lap Time (this works for all Float values)
byte = msgData.subdataWithRange(NSMakeRange(12, sizeof(Float)));
var bestLapTime: Float = 0;
byte.getBytes(&bestLapTime, length: (sizeof(Float)));
//removed
/*var u1 : UInt8 = b[12]; // <- first position
var u2 : UInt8 = b[13];
var u3 : UInt8 = b[14];
var u4 : UInt8 = b[15];
var ddFloat = [UInt8]();
ddFloat = [u1, u2, u3, u4];
let bestLapTime = UnsafePointer<Float>(ddFloat).memory;*/
//print("bestLapTime: \(timetoString(bestLapTime))");

print ("PacketType: \(packetType); Game (state | Session): \(gState[gameState]) | \(sState[sessionState]); NumberParticipants: \(numParticipants); Laps: \(lapsInEvent); BestLapTime: \(timetoString(bestLapTime))");
}
catch let error as NSError
{
print("Error: \(error)");
}

})
NSThread.sleepForTimeInterval(1/50); //time in seconds, for milliseconds use the value you think better
}


If you prefer, you can download the xcode project for test: https://dl.dropboxusercontent.com/u/3713969/PCars_UDP_Console.zip

pjrblue
18-03-2016, 11:14
I got the app running using swift.

AppDelegate.swift (change this part of the code)


func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.

//screen always on
UIApplication.sharedApplication().idleTimerDisabled = true;

//for app launchscreen delay
NSThread.sleepForTimeInterval(2);

return true
}

func applicationWillResignActive(application: UIApplication) {

runUDP = false;
}


udp_read.swift


///
// udp_read.swift
// project_cars_udp
//
// Created by Pedro Ramos on 15/03/16.
//

import Foundation
import Darwin.C

var i: Float = 0;
let server:UDPServer=UDPServer(addr:"255.255.255.255", port:5606);
let run:Bool=true;


func udp_read() -> [UInt8]
{
var _data: [UInt8]? = nil;
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in

let (data,_,_)=server.recv(2048);
_data = data;
})

if (_data == nil)
{
let dataReturn: [UInt8] = [0];
return dataReturn;
}

return(_data)!;
}


TimetoString.swift


//
// TimetoString.swift
// UDP_Test_Console
//
// Created by Pedro Ramos on 14/03/16.
//

import Foundation
let oneHour: Float = 3600; //seconds

enum TimeFormat{
case hour_minutes_seconds_milliseconds
case hour_minutes_seconds
case minutes_seconds_milliseconds
}

func timetoString(timeFloat: Float, formatOutput: TimeFormat) -> String
{
let intmax: Float = Float(Int.max);
if (timeFloat > intmax || timeFloat < 0)
{
switch formatOutput {
case .hour_minutes_seconds:
return "--:--:--";
case .hour_minutes_seconds_milliseconds:
return "--:--:--.---";
case .minutes_seconds_milliseconds:
return "--:--.---";
}
}

let time: NSInteger = max(0, NSInteger(timeFloat));
let milli = NSInteger(1000 * (timeFloat % 1));
let seconds = time % 60;
let minutes = (time / 60) % 60;
let hours = (time / 3600) % 60;

switch formatOutput {
case .hour_minutes_seconds:
return String(format: "%02d:%02d:%02d", hours, minutes, seconds);
case .hour_minutes_seconds_milliseconds:
return String(format: "%02d:%02d:%02d.%03d", hours, minutes, seconds, milli);
case .minutes_seconds_milliseconds:
return String(format: "%02d:%02d.%03d", minutes, seconds, milli);
}
}


udp_variables.swift


//
// udp_variables.swift
// project_cars_udp
//
// Created by Pedro Ramos on 18/03/16.
//

import Foundation

//array for GameState
let gState:[String] = ["EXIT", "FRONT_END", "PLAYING", "PAUSED"];
//array for SessionState
let sState:[String] = ["INVALID", "PRACTICE", "TEST", "QUALIFY", "FORMATION_LAP", "RACE", "TIME_ATTACK"];

var speedMultiplier: Float = 3.6;
public var _packetType: NSInteger = 0;
public var _gameState: String = "";
public var _sessionState: String = "";
public var _currentLapTime: String = "";
public var _lastLapTime: String = "";
public var _bestLapTime: String = "";
public var _splitTimeAhead: String = "";
public var _splitTimeBehind: String = "";
public var _eventTimeRemaining: String = "";
public var _numParticipants: NSInteger = 0;
public var _lapsInEvent: NSInteger = 0;
public var _speed: Float = 0;
public var _rpm: UInt16 = 0;
public var _maxrpm: UInt16 = 0;
public var _gear: String = "r";
public var _participantIndex: NSInteger = 0;
public var _fuelTank: NSInteger = 0;
public var _fuelRemaining: Double = 0;
public var _racePosition: NSInteger = 0;
public var _lapsCompleted: NSInteger = 0;
public var _currentLap: NSInteger = 0;


ViewController.swift (for 2 storyboards, iPad + iPhone)


//
// ViewController.swift
// project_cars_udp
//
// Created by Pedro Ramos on 15/03/16.
//

import UIKit

public var runUDP: Bool = false;
public var viewActive: Bool = false;

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
}

override func viewDidAppear(animated: Bool) {
viewActive = true;
self.updateLabels(runUDP);
updateAll();
}
@IBOutlet weak var Button_iPad: UIButton!
@IBAction func start_stop_iPad(sender: UIButton) {
if (!runUDP){
sender.setTitle("STOP", forState: .Normal);
runUDP = true;
updateAll();
}
else
{
sender.setTitle("START", forState: .Normal);
runUDP = false;
}
}
@IBOutlet weak var Data1_iPad: UILabel!
@IBOutlet weak var Data2_iPad: UILabel!
@IBOutlet weak var Data3_iPad: UILabel!
@IBOutlet weak var Data4_iPad: UILabel!
@IBOutlet weak var Data5_iPad: UILabel!
@IBOutlet weak var Data6_iPad: UILabel!
@IBOutlet weak var Data7_iPad: UILabel!
@IBOutlet weak var Data8_iPad: UILabel!
@IBOutlet weak var Data9_iPad: UILabel!
@IBOutlet weak var Data10_iPad: UILabel!
@IBOutlet weak var Data11_iPad: UILabel!


@IBOutlet private var Data1: UILabel!
@IBOutlet private var Data2: UILabel!
@IBOutlet private var Data3: UILabel!
@IBOutlet private var Data4: UILabel!
@IBOutlet private var Data5: UILabel!
@IBOutlet private var Data6: UILabel!
@IBOutlet private var Data7: UILabel!
@IBOutlet private var Data8: UILabel!
@IBOutlet private var Data9: UILabel!
@IBOutlet private var Data10: UILabel!
@IBOutlet private var Data11: UILabel!

@IBOutlet private var iPadData1: UILabel!

@IBOutlet private var Button: UIButton!
@IBAction func start_stop(sender: AnyObject) {
if (!runUDP){
sender.setTitle("STOP", forState: .Normal);
runUDP = true;
updateAll();
}
else
{
sender.setTitle("START", forState: .Normal);
runUDP = false;
}
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func updateAll()
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone)
{
//iphone
Data1.text = "";
Data2.text = "";
Data3.text = "";
Data4.text = "";
Data5.text = "";
Data6.text = "";
Data7.text = "";
Data8.text = "";
Data9.text = "";
Data10.text = "";
Data11.text = "";
}
else
{
//ipad
Data1_iPad.text = "";
Data2_iPad.text = "";
Data3_iPad.text = "";
Data4_iPad.text = "";
Data5_iPad.text = "";
Data6_iPad.text = "";
Data7_iPad.text = "";
Data8_iPad.text = "";
Data9_iPad.text = "";
Data10_iPad.text = "";
Data11_iPad.text = "";
}


dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) {
while (runUDP)
{
self.processDATA(udp_read());
dispatch_async(dispatch_get_main_queue()) {
self.updateLabels(runUDP);
}
}
}
}

func updateButton(str: String){
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone)
{
//iphone
Button.setTitle(str, forState: .Normal);
}
else
{
//iPad
Button_iPad.setTitle(str, forState: .Normal);
}
}

func updateLabels(b: Bool){
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiom.Phone)
{
//iphone
self.Data1.text = "C: " + _currentLapTime + " / L: " + _lastLapTime + " / B: " + _bestLapTime;
self.Data2.text = "GAMESTATE: " + _gameState + " / SESSIONSTATE: " + _sessionState;
self.Data3.text = "SPLIT - AHEAD: " + _splitTimeAhead + " / BEHIND: " + _splitTimeBehind;
self.Data4.text = "TIME REMAINING: " + _eventTimeRemaining;
self.Data5.text = "RACE POSITION: " + String(_racePosition) + " OF " + String(_numParticipants);
self.Data6.text = "SPEED: " + String(Int(_speed)) + " / GEAR: " + _gear;
self.Data7.text = "RPM: " + String(_rpm) + " / " + String(_maxrpm);
self.Data8.text = "FUEL: " + String(format: "%02d.%01d", Int(_fuelRemaining), Int(10 * (_fuelRemaining % 1))) + " of " + String(_fuelTank) + " LTS";
self.Data9.text = "LAPS - CURR: " + String(_currentLap) + " / COMPLETED : " + String(_lapsCompleted) + " OF " + String(_lapsInEvent) + " TOTAL";
}
else
{
//ipad
//iphone
self.Data1_iPad.text = "C: " + _currentLapTime + " / L: " + _lastLapTime + " / B: " + _bestLapTime;
self.Data2_iPad.text = "GAMESTATE: " + _gameState + " / SESSIONSTATE: " + _sessionState;
self.Data3_iPad.text = "SPLIT - AHEAD: " + _splitTimeAhead + " / BEHIND: " + _splitTimeBehind;
self.Data4_iPad.text = "TIME REMAINING: " + _eventTimeRemaining;
self.Data5_iPad.text = "RACE POSITION: " + String(_racePosition) + " OF " + String(_numParticipants);
self.Data6_iPad.text = "SPEED: " + String(Int(_speed)) + " / GEAR: " + _gear;
self.Data7_iPad.text = "RPM: " + String(_rpm) + " / " + String(_maxrpm);
self.Data8_iPad.text = "FUEL: " + String(format: "%02d.%01d", Int(_fuelRemaining), Int(10 * (_fuelRemaining % 1))) + " of " + String(_fuelTank) + " LTS";
self.Data9_iPad.text = "LAPS - CURR: " + String(_currentLap) + " / COMPLETED : " + String(_lapsCompleted) + " OF " + String(_lapsInEvent) + " TOTAL";
}
if (!b) {
updateButton("START");
}
}

func processDATA(data: [UInt8])
{
//print ("\(data.count)");
if (data.count <= 1)
{
return;
}

//how to get Packet Type
_packetType = NSInteger(data[2]) % 4; //get packetType (0, 1 or 2)
if (_packetType == 0)
{
//how to get GameState & GameSession
let gSession: NSInteger = NSInteger(data[3]);
if (gSession & 0xF < gState.count)
{
_gameState = gState[gSession & 0xF];
}
if (gSession >> 4 & 0xF < sState.count)
{
_sessionState = sState[gSession >> 4 & 0xF];
}

_numParticipants = NSInteger(data[5]);
_lapsInEvent = NSInteger(data[11]);
_eventTimeRemaining = timetoString(convertDataFloat([data[36], data[37], data[38], data[39]]), formatOutput: TimeFormat.hour_minutes_seconds);
_bestLapTime = timetoString(convertDataFloat([data[12], data[13], data[14], data[15]]), formatOutput: TimeFormat.minutes_seconds_milliseconds);
_lastLapTime = timetoString(convertDataFloat([data[16], data[17], data[18], data[19]]), formatOutput: TimeFormat.minutes_seconds_milliseconds);
_currentLapTime = timetoString(convertDataFloat([data[20], data[21], data[22], data[23]]), formatOutput: TimeFormat.minutes_seconds_milliseconds);
_splitTimeAhead = timetoString(convertDataFloat([data[24], data[25], data[26], data[27]]), formatOutput: TimeFormat.minutes_seconds_milliseconds);
_splitTimeBehind = timetoString(convertDataFloat([data[28], data[29], data[30], data[31]]), formatOutput: TimeFormat.minutes_seconds_milliseconds);
_speed = convertDataFloat([data[120], data[121], data[122], data[123]]) * speedMultiplier;
_rpm = convertDataUInt16([data[124], data[125]]);
_maxrpm = convertDataUInt16([data[126], data[127]]);
_gear = convertGear(NSInteger(data[128] & 0xF));
_fuelTank = NSInteger(data[111]);
_fuelRemaining = Double(Float(_fuelTank) * convertDataFloat([data[116], data[117], data[118], data[119]]));

_participantIndex = NSInteger(data[4]); // 464 +
if (_participantIndex > 56)
{
_participantIndex = 0;
}
_racePosition = NSInteger(data[472 + (_participantIndex * 16)]) - 128;
_lapsCompleted = NSInteger(data[473 + (_participantIndex * 16)]);
_currentLap = NSInteger(data[474 + (_participantIndex * 16)]);
}
}

func convertGear(gear: NSInteger) -> String{

if (gear == 0){
return "n";
} else if (gear == 15){
return "r";
} else if (gear > 0 && gear < 9){
return String(gear);
}
return "n";
}

func convertDataFloat(data: [UInt8]) -> Float{
let value = UnsafePointer<Float>(data).memory;
return value;
}

func convertDataUInt16(data: [UInt8]) -> UInt16{
let value = UnsafePointer<UInt16>(data).memory;
return value;
}
}


You need also the files: ysocket.c + ysocketupd.c + yudpsocket.swift (download from: https://github.com/swiftsocket/SwiftSocket/tree/master/SwiftSocket or my previous post https://dl.dropboxusercontent.com/u/3713969/PCars_UDP_Console.zip)
after importing the files you need another file "UDP_Test_Console-Bridging-Header" and inside that, the file was created automatically if you add the files manually

//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "yudpsocket.c"

Also on the Main.Storyboard (or both, if you use 2 storyboards like myself) you need to add 1 Button + 9 to 11 labels (adjust the code for your needs).

result:
230041

jimmyb_84
18-03-2016, 14:50
That's fantastic I need to get my head around it, thank you for sharing your code. It's going to take me a while to understand everything that's going on.

jimmyb_84
19-03-2016, 21:47
I now have a new problem, I've connected the code to the UI now I need to test it to see if it works but cannot access the playstation at the moment, I seem to remember @MikeyTT had a little bit of software that simulated this but I cannot find it for love nor money. I'm sure I won't work but I want to test it anyway.

pjrblue
20-03-2016, 21:34
This is the result of my app for iOS:
https://dl.dropboxusercontent.com/u/3713969/result_final.jpg

jimmyb_84
20-03-2016, 22:45
That's impressive and very fast, it's answered a few of my question just looking at it!

pjrblue
21-03-2016, 10:48
That's impressive and very fast, it's answered a few of my question just looking at it!

It was really fast, one week ago I had never touched the swift language. I have some knowledge of c# and java.

jimmyb_84
21-03-2016, 11:36
It was really fast, one week ago I had never touched the swift language. I have some knowledge of c# and java.

I could tell from your code as you don't actually need ";" in your code but swift still works with them. I wish I'd starting learning C 25yrs ago. I'll get something working eventually but it'll take me a lot longer than you

pjrblue
21-03-2016, 11:47
I could tell from your code as you don't actually need ";" in your code but swift still works with them. I wish I'd starting learning C 25yrs ago. I'll get something working eventually but it'll take me a lot longer than you

It's optional, only needed if we want to write 2 lines of code in the same row. It's a kind of habit for me.

I learned Borland Pascal and Cobol in the years 91/94. Then used some VBA (Excel) for several years, and returned to Visual Basic / C# a few years ago.
I also never learn C/C++, started directly in C#.

I read some of the 801 pages of the book "The Swift Programming Language (swift 2.2) - iBooks (apple store)", to get the basics ... then I google for my doubts.

jimmyb_84
21-03-2016, 14:09
It's optional, only needed if we want to write 2 lines of code in the same row. It's a kind of habit for me.

I learned Borland Pascal and Cobol in the years 91/94. Then used some VBA (Excel) for several years, and returned to Visual Basic / C# a few years ago.
I also never learn C/C++, started directly in C#.

I read some of the 801 pages of the book "The Swift Programming Language (swift 2.2) - iBooks (apple store)", to get the basics ... then I google for my doubts.

The documentation that comes with swift is quite good, I been watching the Stanford videos too which has helped with Xcode more than the actual code part.

Good news I just managed to get it to actually work!

230203

I've only got a single storyboard and have removed the code for the second one, think that is where I was getting confused. Now time to make some adjustments and get my strategy stuff working now I have data to work with. Cannot thank you enough for your help

pjrblue
24-03-2016, 11:03
The documentation that comes with swift is quite good, I been watching the Stanford videos too which has helped with Xcode more than the actual code part.

Good news I just managed to get it to actually work!

230203

I've only got a single storyboard and have removed the code for the second one, think that is where I was getting confused. Now time to make some adjustments and get my strategy stuff working now I have data to work with. Cannot thank you enough for your help

The new Xcode update with swift 2.2 caused problem with the yudpsocket.swift.

We need to change this


@asmname("yudpsocket_server") func c_yudpsocket_server(host:UnsafePointer<Int8>,port:Int32) -> Int32
@asmname("yudpsocket_recive") func c_yudpsocket_recive(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:UnsafePointer<Int32>) -> Int32
@asmname("yudpsocket_close") func c_yudpsocket_close(fd:Int32) -> Int32
@asmname("yudpsocket_client") func c_yudpsocket_client() -> Int32
@asmname("yudpsocket_get_server_ip") func c_yudpsocket_get_server_ip(host:UnsafePointer<Int8>,ip:UnsafePointer<Int8>) -> Int32
@asmname("yudpsocket_sentto") func c_yudpsocket_sentto(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:Int32) -> Int32
@asmname("enable_broadcast") func c_enable_broadcast(fd:Int32)


to this


@_silgen_name("yudpsocket_server") func c_yudpsocket_server(host:UnsafePointer<Int8>,port:Int32) -> Int32
@_silgen_name("yudpsocket_recive") func c_yudpsocket_recive(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:UnsafePointer<Int32>) -> Int32
@_silgen_name("yudpsocket_close") func c_yudpsocket_close(fd:Int32) -> Int32
@_silgen_name("yudpsocket_client") func c_yudpsocket_client() -> Int32
@_silgen_name("yudpsocket_get_server_ip") func c_yudpsocket_get_server_ip(host:UnsafePointer<Int8>,ip:UnsafePointer<Int8>) -> Int32
@_silgen_name("yudpsocket_sentto") func c_yudpsocket_sentto(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:Int32) -> Int32
@_silgen_name("enable_broadcast") func c_enable_broadcast(fd:Int32)


and the application should run.

tgrey
24-03-2016, 13:50
Random question... Does the UDP data streamed out contain what is needed and at a rate appropriate (enough) to create an audio source for butt-kickers based on actual wheel/suspension data? If so, this could finally allow console users to get accurate 4-point rumble feedback for traction loss etc.

Gassolini
24-03-2016, 14:03
^ Yes, that should be possible. There's acceleration, suspension movement, car position and orientation, grip/slip data (not proper STM data though) and more. That ought to be enough to synthesize audio streams to drive butt-kickers in a physics derived fashion. Incidentally, I started on such an app a while back, but other things took priority...

jimmyb_84
24-03-2016, 22:57
The new Xcode update with swift 2.2 caused problem with the yudpsocket.swift.

We need to change this


@asmname("yudpsocket_server") func c_yudpsocket_server(host:UnsafePointer<Int8>,port:Int32) -> Int32
@asmname("yudpsocket_recive") func c_yudpsocket_recive(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:UnsafePointer<Int32>) -> Int32
@asmname("yudpsocket_close") func c_yudpsocket_close(fd:Int32) -> Int32
@asmname("yudpsocket_client") func c_yudpsocket_client() -> Int32
@asmname("yudpsocket_get_server_ip") func c_yudpsocket_get_server_ip(host:UnsafePointer<Int8>,ip:UnsafePointer<Int8>) -> Int32
@asmname("yudpsocket_sentto") func c_yudpsocket_sentto(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:Int32) -> Int32
@asmname("enable_broadcast") func c_enable_broadcast(fd:Int32)


to this


@_silgen_name("yudpsocket_server") func c_yudpsocket_server(host:UnsafePointer<Int8>,port:Int32) -> Int32
@_silgen_name("yudpsocket_recive") func c_yudpsocket_recive(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:UnsafePointer<Int32>) -> Int32
@_silgen_name("yudpsocket_close") func c_yudpsocket_close(fd:Int32) -> Int32
@_silgen_name("yudpsocket_client") func c_yudpsocket_client() -> Int32
@_silgen_name("yudpsocket_get_server_ip") func c_yudpsocket_get_server_ip(host:UnsafePointer<Int8>,ip:UnsafePointer<Int8>) -> Int32
@_silgen_name("yudpsocket_sentto") func c_yudpsocket_sentto(fd:Int32,buff:UnsafePointer<UInt8>,len:Int32,ip:UnsafePointer<Int8>,port:Int32) -> Int32
@_silgen_name("enable_broadcast") func c_enable_broadcast(fd:Int32)


and the application should run.

Thanks for the heads up, I had actually fixed that issue after the update, meant to let you know.

I've started a new job so not giving me much time to code

Chawabax
13-04-2016, 20:48
Hi, I'm not a player (sigh... problems with my eyes at the moment), I'm a streamer.
Did an app to overlay F1 style graphics on a live broadcasts for my team. It's working, it improves live coverage quality but... it needs a lot of work during broadcast because it is completely manual !!! I mean... someone has to update drivers data (positions, laptimes, best laptimes, last lap times, pits, tyres and so on... as much as you can).
I think you can guess that updating laptimes manually is quite crazy... it is true... not able to do it. Still the final result is not bad. (example: http://i64.tinypic.com/34iiyzc.jpg)

Now I'm trying to use it with Project Cars... I can do it already of course... but I'm trying to have data from the game, reading UDP and passing data to my app. Maybe using a CSV file.
App is for Android OS (don't ask me why :rolleyes: ). I'm able to run it on a PC using BlueStacks so I can not read UDP data with my app since Bluestacks is blocking this type of com.
by the way... Bluestacks screen goes in overlay on Xsplit + race video and the final result sent to Youtube. Complicated but... it works.

OK, back to my problem
I don't want (actually) to install VB.net, C or other heavy SW. I started from Excel because I found this old example on the web (ctrl_lan.pdf (http://cp.literature.agilent.com/litweb/pdf/16000-95012.pdf)).
I realized that I had to move from wsock32.dll to ws2_32.dll library
I realized that this example is speaking about Stream sockets so I moved to Datagram sockets type.
I'm able to initialize the dll, to open the socket, to bind... even to try to read (recvfrom function) but, without errors, the reading buffer is always empty !!! And no errors at all.
Checking VB.net and C examples seems that they are using the same functions and the same library
My PS4 is sending data via UDP: my Android device is able to receive them (using PCarsTT app for test) and my laptop is able too (using Crew chief app for test) so this is not a network/setting problem
OS Windows 8.1, Office 32bit

Do someone knows the reason why Excel is not able to receive data via UDP?
Do someone knows the reason why I am :D not able to receive data via UDP?
Do someone can suggest something? (I can post the code if needed but I suppose I'm missing something basic)

Thx, Paolo

pjrblue
14-04-2016, 11:20
Hi, I'm not a player (sigh... problems with my eyes at the moment), I'm a streamer.
Did an app to overlay F1 style graphics on a live broadcasts for my team. It's working, it improves live coverage quality but... it needs a lot of work during broadcast because it is completely manual !!! I mean... someone has to update drivers data (positions, laptimes, best laptimes, last lap times, pits, tyres and so on... as much as you can).
I think you can guess that updating laptimes manually is quite crazy... it is true... not able to do it. Still the final result is not bad. (example: http://i64.tinypic.com/34iiyzc.jpg)

Now I'm trying to use it with Project Cars... I can do it already of course... but I'm trying to have data from the game, reading UDP and passing data to my app. Maybe using a CSV file.
App is for Android OS (don't ask me why :rolleyes: ). I'm able to run it on a PC using BlueStacks so I can not read UDP data with my app since Bluestacks is blocking this type of com.
by the way... Bluestacks screen goes in overlay on Xsplit + race video and the final result sent to Youtube. Complicated but... it works.

OK, back to my problem
I don't want (actually) to install VB.net, C or other heavy SW. I started from Excel because I found this old example on the web (ctrl_lan.pdf (http://cp.literature.agilent.com/litweb/pdf/16000-95012.pdf)).
I realized that I had to move from wsock32.dll to ws2_32.dll library
I realized that this example is speaking about Stream sockets so I moved to Datagram sockets type.
I'm able to initialize the dll, to open the socket, to bind... even to try to read (recvfrom function) but, without errors, the reading buffer is always empty !!! And no errors at all.
Checking VB.net and C examples seems that they are using the same functions and the same library
My PS4 is sending data via UDP: my Android device is able to receive them (using PCarsTT app for test) and my laptop is able too (using Crew chief app for test) so this is not a network/setting problem
OS Windows 8.1, Office 32bit

Do someone knows the reason why Excel is not able to receive data via UDP?
Do someone knows the reason why I am :D not able to receive data via UDP?
Do someone can suggest something? (I can post the code if needed but I suppose I'm missing something basic)

Thx, Paolo

I have a possible solution, let's me explain the idea.

I made a DLL using VS and imported that in Excel (used the Office XP 2002 version, is the only I have at work).
I used this tutorial (http://www.geeksengine.com/article/create-dll.html) and it's working, everytime I press a button the DLL reads the data and return to an array.

Dll Code


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Net;
using System.Net.Sockets;

namespace ClassTest4Excel
{
public class Class1
{
private const int listenPort = 5606;

public string returnDateTime()
{
//only for DLL test
DateTime dt = DateTime.Now;

return
dt.Year.ToString() + "/" +
dt.Month.ToString() + "/" +
dt.Day.ToString() + " - " +
dt.Hour.ToString() + ":" +
dt.Minute.ToString();
}

public byte[] returnByte()
{
UdpClient listener = new UdpClient(listenPort);
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, listenPort);
byte[] receive_byte_array;
try
{
receive_byte_array = listener.Receive(ref groupEP);
listener.Close();
return receive_byte_array;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
listener.Close();
}
return null;
}
}
}


VBA Code


Private Sub CommandButton1_Click()
Dim objImport As ClassTest4Excel.Class1
Set objImport = New ClassTest4Excel.Class1

Dim str As String
TextBox1.Text = objImport.returnDateTime()

Dim bytes() As Byte
bytes = objImport.returnByte()

Dim n As Integer
If (TextBox3.Text = "") Then TextBox3.Text = "1"
n = CInt(TextBox3.Text)
TextBox2.Text = bytes(n)
End Sub


Know I need to get it in another computer using this tutorial (http://www.geeksengine.com/article/register-dll.html).

If you want to try it by yourself, you can download the .DLL and .TLB, use this link: https://dl.dropboxusercontent.com/u/3713969/ClassTest4Excel.zip
Problem: If not data received the VBA keeps waiting for data.

Result/Test
https://dl.dropboxusercontent.com/u/3713969/result.jpg

Edit:
For the dll register I did this.
1. Copied the files ClassTest4Excel.dll + ClassTest4Excel.tlb to c:\windows\system32
2. Opened Command Prompt (Win+R >> cmd)
3. Used the command: cd\Windows\Microsoft.Net\Framework\v4.0.30319\ >> Enter
4. command: RegAsm c:\windows\system32\ClassTest4Excel.dll /tlb:c:\windows\system32\ClassTest4Excel.tlb /registered >> Enter
5. Now in Excel added the Reference (menu >> Tools >> Reference ...) and follow the tutorial link.

Chawabax
14-04-2016, 16:34
For the dll register I did this.
1. Copied the files ClassTest4Excel.dll + ClassTest4Excel.tlb to c:\windows\system32
2. Opened Command Prompt (Win+R >> cmd)
3. Used the command: cd\Windows\Microsoft.Net\Framework\v4.0.30319\ >> Enter
4. command: RegAsm c:\windows\system32\ClassTest4Excel.dll /tlb:c:\windows\system32\ClassTest4Excel.tlb /registered >> Enter
5. Now in Excel added the Reference (menu >> Tools >> Reference ...) and follow the tutorial link.

Had some problems with dll registration and I found that:

1. I can not put these 2 files in ...\system32\ because RegAsm was not able to "locate" them. I found an explanation on the web speaking about fully 32bit and 64bit dependencies or something like... but it is not so important.
2. So I moved your 2 files in a new folder, opened Cmd Prompt (as Administrator), used CD DOS command to move into my new folder and called RegAsm from there with full path. It worked, correct registration.
3. In Excel, in the macro editor, I added the reference to the dll: the new dll was already there, had only to enable it. OK, done
4. pasted the VBA code as a macro (without using forms but it's the same) and run it.... automation error!! Debug is pointing the line "Set objImport = New ClassTest4Excel.Class1"
5. Again on the web I found that to be able to reference to external dll (not only when coding but also in runtime mode), I have to register the new dll with /codebase flag

now it is working.... have only to undertand what it does :D :D :D :D

thanks a lot !

pjrblue
14-04-2016, 17:58
The dll file reads the UDP data to a byte array and you "call it" when needed.
Now you need to implement in the VBA a cycle to read it each time you want or always.

This is the read you need to repeat everytime you need the information


Dim bytes() As Byte
bytes = objImport.returnByte()


To understand the UDP data, see the first post.

If you have any doubts ask so we can help you.

Chawabax
14-04-2016, 18:45
The dll file reads the UDP data to a byte array and you "call it" when needed.

I was joking, I know what it does ;)

have to understand the UDP data format, utf-8 and so on... again thanks a lot ! :)

Chawabax
16-04-2016, 23:35
...have to understand the UDP data format, utf-8 and so on... again thanks a lot ! :)

http://i64.tinypic.com/357rcpu.jpg

Far from being a good job... still this Excel file is doing what I need :cool:

I can have a time monitor on my laptop with driver names, positions, sector times, laptimes, current laps.
I did some complicated calculations to have distance (time format MM:ss.mmm) between drivers: was not able to find this information coming from the game, I found only distance in meters. Maybe with some timer I can do a better job but I'm quite satisfied with this approximate calculation.

Ther is also a DEGUB datasheet with all bytes of the UDP packets.

As soon as I will be able to find a place online where to store the file, I will share. Found:---> PCars_UDP_Rec_V04.zip (http://www.f1italianteam.com/media/kunena/attachments/482/PCars_UDP_Rec_V04.zip) @f1italianteam.com

The code is not so fast, not oprimized and No error checking.
But maybe can be a good start for someone who wants to develop it.

Chawabax
25-04-2016, 15:35
I have a problem and I'm not able to find a reasonable explanation (yes, I found a workaround but not always working)

I'm used (VBA) to select the packet type with this formula:
Packet type = byte(2) mod 4
where "byte" is the array with UDP data inside

0=standard
1= 16 drivers
2= other drivers

I'm able to receive all of them with different rates, as should be.
I gave my excel file to another guy (using PS4 the same as me) and he was never (NE-VER) able to receive packets 1 & 2, only packet 0
We tried with different settings on the PS4 for UDP but nothing changes

I cannot believe that he's only not lucky... there is something I'm missing.... what?

pjrblue
26-04-2016, 15:06
@Chawabax, is really difficult to say what is missing.
But starting by the basics, he has the game updated to the last version? Is testing the file in a race/weekend event?

Chawabax
27-04-2016, 11:43
@Chawabax, is really difficult to say what is missing.
But starting by the basics, he has the game updated to the last version? Is testing the file in a race/weekend event?

He's testing both in offline weekend race (trest, qualify, warm-up, race), and in online races.
With less or more (only offline) than 16 drivers
with UDP setting from 5 to 8
Console PS4: so.... I think the game is updated because if not you can not play online... but I will make him check to be sure

pjrblue
27-04-2016, 14:23
He's testing both in offline weekend race (trest, qualify, warm-up, race), and in online races.
With less or more (only offline) than 16 drivers
with UDP setting from 5 to 8
Console PS4: so.... I think the game is updated because if not you can not play online... but I will make him check to be sure

Yesterday I played Project Cars on X1 in a race event (test + qualify + race 6 laps) and I only received for 5 or 6 times the data (type 1), using my app for android.
If I have time today or tomorrow I test your excel file on my PS4, as soon I test it I post here. I have a PC with W10 x64 and the Office 2010.

On PS4 you can play without updating, of course you are online on PSN but cannot play online with the game.

Chawabax
27-04-2016, 17:00
..but cannot play online with the game.

this is !
He was able to play online with his friends (but not receiving pkt.1) so the game should be updated

P.S. my excel file starts looking for packet n.1 - if not receiving it, excel is going on checking (there is a little counter) without showing laptimes or sector times
actually I changed this initial procedure: now I force initial driver names

pjrblue
28-04-2016, 10:07
P.S. my excel file starts looking for packet n.1 - if not receiving it, excel is going on checking (there is a little counter) without showing laptimes or sector times
actually I changed this initial procedure: now I force initial driver names

This modification change the results of him?

Edit: I download your file and tested on my PS4 with the W10 x64 + Office 2010 and can't get the packet also. Don't have more time now, perhaps tomorrow or in the weekend I give it a try again.

Chawabax
28-04-2016, 23:11
This modification change the results of him?
Edit: I download your file and tested on my PS4 with the W10 x64 + Office 2010 and can't get the packet also. Don't have more time now, perhaps tomorrow or in the weekend I give it a try again.

Tonight, during a 1 hour long race broadcasted LIVE, the guy was able to receive 1 (ONE !!!) packet type 1. So... something really strange is happening.

I'm able to receive 1 packet at least every lap (Windows 8.1, Office 2013, PS4 connected with cable, NAT 2, No DMZ, Laptop connected with Wi-Fi, game on disc)
My friend received only 1 in 10 hours of tests (Windows 8.1, Office 2010, PS4 connected with cable, NAT 2, No DMZ, Desktop connected with cable, game downloaded digital version)

Since we know exactly all the drivers in the race and their starting position, I can "force" their names at the beginning of the race but if someone disconnects... all rankings are gone :(

pjrblue
29-04-2016, 19:03
I tested the excel file again, I received the data 40.627 times and 0 times the packet type 1.
The only thing in common is the office version, I also have the 2010 version.

Chawabax
29-04-2016, 21:59
I tested the excel file again, I received the data 40.627 times and 0 times the packet type 1.
The only thing in common is the office version, I also have the 2010 version.

Thank you for the extensive test, really appreciated.
I ask you the last things:
1) can you confirm me that you have the game on disc?
2) can you confirm me that that you are able to receive these data (drivers name and best laps) using other apps?
3) do you have 32bit or 64bit Office version installed? (I have 32bit)

I will try to analyze data packets received to understand what's happening (maybe the right byte is in a different position... for some strange reason)