From: Annick et Jean-P. <jpm...@fr...> - 2008-05-05 21:21:51
|
Hi, Wolf-Dieter. >> But at this point, we should look at future needs too: >> Give to the robot a torcs version number and torcs back a robot >> (interface-type) number. At last, I found some time to examine closely your long mail explaining your request ! > having an independent possibility to identify the interface to use helps to run a mixed mode > for interfaces between robots and TORCS. > Mixed mode means here to be able to use different TORCS-Interfaces > (by the robot) or to load robots of different versions (by TORCS). By "interface version exchange" between Torcs and the robots, do you mean something like (as an example) : - in the robot DLL entry points, have one to get the robot interface version, like : extern "C" void getItfVersion(int* major, int* minor) { major = 2; minor = 0; } // Interface version 2.0 - in the robot DLL entry points, have one to set the Torcs version, like : extern "C" void indicateTorcsVersion(int major, int minor, int patch) { TorcsMajor = major; TorcsMinor = minor; TorcsPatch = patch} // TorcsMajor, Minor and Patch being global variables of the robot - make Torcs call these 2 entry points before calling the robot initialization entry point extern "C" <robot name>(tModInfo* modInfo), in order, * for the robot, to be able to have a different behaviour from one version of Torcs to another, * for Torcs to initialize the robot differently from one interface version to another ? > Why change the interfaces? One example is your work to use bots with variable numbers of drivers. > Or to make the interface more flexible. > > Here some informations from a coder using other languages then C/C++: > > If you use only standard robots (written in C/C++) you can change a TORCS interface structure, recompile all and use it. >But if you use an other programming language (like Delphi(windows)\Larazus(linux) = Object Pascal), >you have to change your code (at least update the translations of the header files). >You will do this work only, if you want to use the new features. > > Example: With the change of TORCS 1.2.4 to TORCS 1.3.0 the structure of the own pit was changed: > > (Delphi code:) > {$IFDEF TORCS_V1.3.0} > TTrackOwnPit = packed record > Pos: TTrkLocPos; // Center of the pit position > PitCarIndex: Int; // Index of Car using the Pitbox > Lmin: Tdble; // Pitting area length min > Lmax: Tdble; // Pitting area length max > FreeCarIndex: Int; // Index of next free car entry > Car: PCarElt; // Driver's car link > end; > {$ELSE} // means V1.2.4 > TTrackOwnPit = packed record > Pos: TTrkLocPos; // Center of the pit position > State: Int; // not yet used (will be used for pit sharing within teams) > Lmin: Tdble; // Pitting area length min > Lmax: Tdble; // Pitting area length max > Car: PCarElt; // Driver's car link [TR_PIT_MAXCARPERPIT] > end; > {$ENDIF} > > What was bad here? If you put new things to the end only, not between, you could use the old code in the most cases unchanged. You get a pointer to the start of the structure and it does not affect you, that the block is greater now, because the sender allocates/frees the memory, you use it only (or not, no pitsharing): > > TTrackOwnPit = packed record > Pos: TTrkLocPos; // Center of the pit position > State: Int; // NOT USED BUT KEPT! > Lmin: Tdble; // Pitting area length min > Lmax: Tdble; // Pitting area length max > Car: PCarElt; // Driver's car link [TR_PIT_MAXCARPERPIT] > PitCarIndex: Int; // NEW: Index of Car using the Pitbox > FreeCarIndex: Int; // NEW: Index of next free car entry > end; > > The central structure used now is > > TCarElt = packed record // car.h > Index: Int; // car index > Info: TInitCar; // public > Pub: TPublicCar; // public > Dummy1: Int; // Alignment to 8 Byte instead of 4 Byte using Delphi 5 > Race: TCarRaceInfo; // public > Priv: TPrivCar; // private > Ctrl: TCarCtrl; // private > PitCmd: TCarPitCmd; // private > Robot: PRobotItf; // private > Next: PCarElt; // Next in List > Dummy2: Int; // Alignment to 8 Byte instead of 4 Byte using Delphi 5 > end; > > Here all structures are combined ("by value"), which leads to a complete rework, if one of the structures is changed. > > In many other systems an other strategy is used: > > TCarElt = packed record // car.h > Size: Int; // Number of pointers filled in // 4 Byte > Index: Int; // Car index // 4 Byte so we are again on the 8 byte alignment! > Next: PCarElt; // Pointer to Next in List // 4 Byte or 8 Byte ... > Info: PInitCar; // Pointer to a TInitCar structure > Pub: PPublicCar; // Pointer to a TPublicCar structure > ... > Robot: PRobotItf; // private > end; > > Using a list of pointers, you are free to let all structures grow without the need to change all old modules even if the new data is not used (New features/pointers are at the tail only!) So, if I try and sum up, you are stating/suggesting that : - until now, robot interface was changed in Torcs with no real care about non C/C++ robot code, assuming that "one has only to recompile with new headers, and all's right" ; and this is a bad assumption for Delphi-coded robots. - in next Torcs versions, such brutal changes should be avoided, - but the only solution would be to make ONE last brutal change : use pointers on sub-structures as main structures fields, in place of sub-structure values (ex: Next: TCarElt => Next: PCatElt) ; then, no (less?) alignement issues when adding new fields in sub-structures ... You may be right, but my opinion is that it would probably imply big changes in Torcs code for that "brutal" (;-) last big interface change ... and that this big changes would be difficult to support against the main stream of "I'm not interested ; it may imply some unstability ; so I prefer not". But you are right, it should be quite easy to try and minimize robot interface changes ... with some mail exchanges ... > Ok, we see there may be possible alternatives to be used by future TORCS releases. > > At the moment, you want to change the size of the tModInfo structure, to fit robots with more than 10 drivers. > Now, the first call of the module (dll) is used to get the drivers names and with the changed version this would remain. > After a call to the new interface 'extern "C" unsigned int <module name>_NbItf()', TORCS allocates > the memory needed and gives it to the robot to get the names. > But this is not consequently! What do you mean by "consequently" ? > A good robot should use the names, defined in the teams XML-file. So the user can change these to fit his own needs. >Therefore all robot programmes would have to write code to read out this file. >In future, having the possibility to use robots with a variable number of drivers, you could get this information from this file too! > Why not make TORCS read this file (only one implementation of the code is needed). >You find the names, the descriptions, the teams, the car types, the racing numbers,... and the required number of drivers. >Then allocate the memory, copy the pointers of the names and give it to the robot, together with the number of drivers to use. You are right, all this static could be directly read by Torcs in the robot XML file. But by "static", I mean : that never change during races. >If the robot needs special names, it can change the names (set new pointers). If not, it only sets the function pointers for TORCS. >If the robot knows, what version of TORCS-Interface is used, it could switch between this possibilities without much work to do. >Having the version information (of the TORCS interface) in a structure, used for other purposes too (the "gfid" field of tModInfo >structure for example), means, you are not longer free to uses different structures because you have to know, where the imformation is! Can you explain me the purpose of the "gfid" field ? Of course, I was not meaning to use it for multiple purposes at the same time ! > So a first contact with a function like "robot_interface_version = version(torcs_interface_version)" is a good idea. OK, this seems quite close to what I was proposing in the first line of this mail. >All following steps can be done, knowing the interfaces. >You will not get more freedom to change interfaces in an distributed system running 24*7. > Here a question: Why to use robotname ('extern "C" unsigned int <module name="robotname">_') as part of the function names? > So I have to change my code at many places, if I want to use an old and a new version of my bot, to compare my work! Fixed names like "robot_module()", "robot_version()" , ... would work too, you could combine/replace it with the modules name before you use it in your lists. Yes, you are right. It could be simpler. > A common principle used in most systems is the sandwich pattern: > Allocate resource > Use resource > Free resource > > To have interfaces following this pattern will help to make coding easy. > > InitModul // allocate resources for the modul > InitTrack // allocate resources for the track > InitDriver // looped over all drivers to allocate resources for the driver > NewRace // register driver at teammanager or teammates (allocated in InitDriver) > ... > EndRace // looped over all drivers to unregister or do statistics ... > Shutdown // looped over all drivers to free resources of the driver > FreeTrack // free resources for the track > FreeModul // free resources for the modul > > At the moment we have > > InitFunc // allocate resources for the modul > InitTrack // allocate resources for the track > NewRace // allocate resources for the driver > ... > Shutdown // free resources for the driver Actually, under Linux only, you have also the "FreeModul" : when unloading the modules, if a "<module name>Shut" function exists (of type tfModShut defined in tgf.h), it is called. BUT not under windows (I don't know why, may be there's another special Windows trap ...) But you are right, the sandwich pattern should be fully implemented ! > If you allocate resources for a teammanager (at InitFunc), that is used by all drivers and uses track informations, when do you free it? > A call to Shutdown is robot specific, you don't know if you are the last! Right ! > Same is writing a logfile of communications between all robots and the teammanager in one file to keep the chronological order. > Open in InitModul and close in FreeModul is so easy. > Yes, there are ways do solve this, on windows and linux, but why not use a calling sheme, making it easy to understand (and use) > and beeing independent from windows or linux? Right ! > At the moment, the need for changes of the interfaces is not very urgent. But if we now install the exchange of the two interface versions > (not TORCS version or robot version!), we can profit by this, when the need grows. > And it will grow, if the game will be online playable! And the advantage of being able to use mixed modes (of interfaces) too. > Not all players will have to use the same (last) version. > If you look how to realize it, you will see, that it would be a little step > to do now. Oh, oh. But there, you are talking about BINARY compatibility between different versions of Torcs and one version of a robot ... As far as I understand, we can achieve it with "robot module entry points stuff" listed above, but what about the calls from the robots to Torcs itself ? At least the robottools DLL should also be kept binary compatible ... And there may be others (I'm not so much aware about robots code) ... >But the benefit later will be great. And torcs will be changed slowly, step by step, no revolutions needed, but many steps! Good method. I agree with that. Regards, Jean-Philippe. |