=============================================================================== This log contains a summary of changes performed in the cpp-branch to transition Eternity from C to C++. cpp-branch spans SVN revisions from 1344 to 1408. =============================================================================== ------------------------------------------------------------------------------- 01/08/11 Changes in preparation for v3.40.00 release: I cleaned up the weird and unnecessary cast back to memblock_t when preparing allocated memory for return from Z_Malloc, as I don't like it referring to the block's memory as if though it were a memblock_t. With essel's assistance we have enabled portal overlay specification through ExtraData sector records; however, I guess SoM didn't test it enough and the portal overlay system malfunctions severely once there is more than one distinct overlay in visibility - only the highest numbered sector's overlay seems to function correctly according to essel. This will be live in the new release, but nobody's going to get much use out of it until one of us can figure out what is really going on. Since I didn't write the code, I am at a disadvantage with figuring it out personally. I disabled the code that tries to adjust the upper clipping boundary for the renderer to the bottom of the console to avoid overdraw, because it isn't working in widescreen aspect ratios for some reason. This may not be worth fixing in the long run. CSonicGo found an old glitch dating all the way back to SMMU - if netdemos were started with -playdemo or the playdemo console command, then EE would remain in netgame mode after the demo stopped and would crash at some point later, seemingly arbitrarily, by attempting to execute the netget function pointer in the low-level netcode with the pointer still set to NULL. This was fixed by refactoring G_CheckDemoStatus so that it always properly frees the demo buffer, unsets netgame, and calls G_ReloadDefaults, even if the game engine is in "singledemo" mode, as it is when using playdemo. Copyright dates and version info have been updated for the impending release. cpp-branch is ready for merger back into trunk. ------------------------------------------------------------------------------- 01/03/11 My beta testers found a crash - if you used the addfile command, the program would segv the next time a sound tried to play. My recent changes to the sound engine made the code to stop channels in i_sdlsound.cpp capable of trying to free the channel more than once. This was ending up with a call to Z_ChangeTag with a NULL parameter. Besides fixing the sound engine flaw itself, I also added a NULL pointer check to Z_ChangeTag. ------------------------------------------------------------------------------- 01/02/11 QStrCat was *STILL* exhibiting broken behavior, this time as a consequence of using the sum of the lengths calculated at the beginning to reset the qstr's index. Instead, I have replaced all instances of this being done with calls to strlen(), which, while maybe slower, has its head on straight with respect to 0-vs-1-based numbers in a way I cannot seem to reliably manage. It's far better to cut myself out of the loop with this and rely on something I know to be correct for the future. I've begun to understand why writing your own string library can be painful ;) ------------------------------------------------------------------------------- 01/01/11 Happy New Years! While working on some features for the next upcoming 10th anniversary release, I ran headlong into a bizarre and hard-to-crack heap corruption issue. Turned out my strcat and insert methods in the qstring module were miscalculating the amount of needed space by ONE character and this was suddenly, after YEARS of proper functioning, causing a problem. Man I hate when that happens :P Took me 3 solid hours of heavy debugging to figure out precisely what was going on, but now I've learned some very handy techniques for debugging using the MSVC++ debug heap that will significantly shorten the time in the future if this should happen again. I made the S_ISDIR macro actually use the parameter it was being sent, and not assume that the stat structure it's being used on is named sbuf. This was only working because they WERE all named that, by virtue of copypasta. I also added a QStrNormalizeSlashes method, since M_NormalizeSlashes cannot be safely called directly on a qstring's buffer pointer - this would leave the strlen inconsistent with the insertion index, causing improper reallocation and concatenation behavior - in the event that duplicate slashes were removed from the string. -game and -iwad may now be used with simple IWAD names (ex: doom.wad) and if the proper IWAD paths are configured in system.cfg, Eternity will select the best matching IWAD. For example, if I have configured IWAD paths for DOOM Registered and Ultimate DOOM, and start Eternity using only -iwad doom.wad with no doom.wad file in immediate visibility to the program (ie. in "." or in the exe directory), Eternity will select the Ultimate DOOM configured IWAD automatically, because it is the "best" version for the game. In addition to this, Eternity also now supports the DOOMWADPATH environment variable, and will find IWADs on that path as well if no paths are configured or available in system.cfg first. ------------------------------------------------------------------------------- 12/30/10 Fixes galore: 1. Sound engine fix for a problem inherited from trunk -- channel data pointers can and should be cleared as soon as the audio update thread is finished with them. This eliminates the extra loop in I_StartSound entirely and keeps sound channels freed up. 2. Some (Mobj *) casts in Lee's friendly monsters code in p_enemy were no longer technically correct or safe, and have been replaced with proper dynamic_cast calls. 3. Unnecessary and dangerous casts of PointThinker pointers to Mobj * and then immediately back to PointThinker * have been entirely eliminated - I thought I got all of these previously but some slipped through the cracks due to using slightly different idioms. They're probably actually safe since the pointers are never dererenced between all that casting, but it's far better not to tempt the hoary bearded gods of C++. 4. __attribute__((packed)) should not be used in C++, as it is allegedly syntactically incompatible with C++, though GCC had yet to actually issue a single complaint about its presence. All instances have been replaced by changing the #ifdef _MSC_VER previously used to control all usage of the #pragma pack(push, 1) and #pragma pack(pop) directives to use: #if defined(_MSC_VER) || defined(__GNUC___) - Since GCC has supported #pragma pack for quite a while now, this is by far the most preferable solution. ------------------------------------------------------------------------------- 12/29/10 Added a "Buddha," or immortality mode, similar to the one in ZDoom, since essel requested it a while back with the justification that it is beneficial to testing of maps for gameplay balance purposes, as one can tell at what point a player of equal skill might start to run low on health, without being in jeopardy of actually dying and then having to return to full health via the resurrect command to continue playtesting. ------------------------------------------------------------------------------- 12/28/10 Demo compatibility fixes courtesy of Doomworld Forums user 4mer: * All subroutines which were split out of PIT_CheckThing for reuse in the 3D object clipping module MUST use clip.thing directly and not accept it as a function parameter, due to the potential for (undefined) reentrant calls to take place which overwrite members of the global clip struct. The non-reentrancy of DOOM's clipper is well-known to me, as I have struggled with it before. However, the fact that just changing some lines of code into a subroutine and passing a value to them through a parameter can break demo sync is insideous and has serious implications. Further changes to this code in the future MUST take note of this and be cautious to the point of paranoia. This included the routines P_SkullHit, P_Touch, and others. P_SkullHit was in fact the cause for the desync in the Speed of Doom TAS demo earlier mentioned, with the failed Arch-vile jump. Several Lost Souls (the greatest nightmare for demo sync ever) were in the area, and one or more of them were suffering reentrant clipping behavior. * mlook math must not be applied during playback of non-Eternity demos. I am entirely unsure of how I overlooked this, as a similar issue was repaired in the renderer years ago which was causing the entire screen to be slightly off the horizontal when at player->pitch == 0 (an extra pixel of sky at the top was the only reliable indicator of this, and is how it was caught in the first place). This roundoff error was apparently so small that it somehow virtually never caused demo desyncs. However, we're not going to tolerate such obvious problems when we can avoid them quite easily ;) * Some code earlier adapted from PrBoom-Plus while fixing Okuplok's SOD MAP33 demo had been deliberately changed to use rigorous, correct handling of mobj reference counts via calls to P_SetTarget, but according to 4mer this does disturb demo sync. Since this code is ONLY called during 200-202 demo playback, and because it seems to maintain a reference count balance that is in favor of an object NOT being freed even when it could be, it should be reasonably safe to allow it to slide here. In NO OTHER code in the engine should mobj references be directly assigned! Reference counting is an absolute necessity. * The same-subsector short-circuiting optimization from MBF when checking lines of sight, which has been a source of demo compatibility problems in the past already, should *NOT* be applied during 200 - 202 demo playback. cph's prior fix adapted from an early PrBoom revision only addressed not applying this code during v1.9 demo playback. I guess he failed to realize this change was from MBF and not BOOM, but the "killough 11/98" comment above it is a dead giveaway, as Lee was quite busy on MBF at the time, as I clearly remember from my own personal experiences as a beta tester on MBF. I had just started college 2 and a half months earlier, so that time is quite vivid in my mind. Eternity itself was still in diapers back then ;) A barely-evolved-past- DeHackEd hack of BOOM which contained an Imp that fired modified Arachnotron plasma that left blur trails. Quite a way we've come since then, eh? ------------------------------------------------------------------------------- 12/27/10 I accidentally lost the code to restore Cardboard floor and ceiling heights when loading savegames which was causing a really weird infinite horizon glitch to appear on any sector which had been saved with an altered floor or ceiling height after reloading the save. Thinker and soundtarget pointers in sectors were also not being nullified, which although unlikely, probably could have lead to some interesting problems. GCC fixes: * InBuffer::Read had a type conflict between its declaration and definition. * class Mobj needs declaration in a_small.h even if EE_NO_SMALL_SUPPORT is defined. * void *'s need to be cast through size_t as an interim solution for the %p formatting code in psnprintf, although a more ideal solution is of course to add proper support for 64-bit pointers. That is a TODO, however. * All extern declarations of FloatBobOffsets need to be consistent. * SMMU keybinding code needed proper constness constraints. * EE_NO_SMALL_SUPPORT has to be #ifndef'd in amx.cpp and amx_core.cpp. * A goto in f_finale.cpp crossed the declaration of a variable, which isn't allowed in C++ (except if you're Microsoft). * Netcode functions also have to cast offsets through size_t, as GCC assumes through lack of information that these values could be 64-bit precision (they never are in this case, however). * libConfuse can longer "cram" defaults for all types of fields into its single void *def field in cfg_opt_t -- instead I had to rewrite the library to use a separate default field for each type of cfg_opt_t. Not ideal, and could probably benefit from object orientation later on. * DavidPH provided a CMakeLists patch which got this whole thing compiling again. Super-special thanks to him for his help and patience. ------------------------------------------------------------------------------- 12/26/10 With completion of thinker serialization and a rewrite of the rest of the savegame module, r1369 has become the first compiling revision of Eternity under C++! I have also decided to drop the C prefix from all Eternity class names, as I hadn't applied it universally and it was really starting to wear thin already. Leave that crap to Microsoft :P ------------------------------------------------------------------------------- 12/25/10 Ho-ho-hoooo, Merrrrrry Christmas! Serialization methods have been finished for all CThinker descendants except for CACSThinker, which is quite complicated, and mobj_t, which is the MOST complicated of all. ------------------------------------------------------------------------------- 12/24/10 Work continues on the process of thinker deserialization. I have, as discussed previously, moved the enumeration facility back into CThinker and up out of mobj_t, since other thinker types may need access to it in the future and I want to code this in as generic a manner as possible. The mobj_p table used to track mobj pointers for indices during savegame loading has been refactored into thinker_p so that it works on all descendants as necessary. A virtual deswizzle method has been added to CThinker so that pointer fixups can be done in a separate second pass, which is necessary since ALL thinkers must be instantiated before references to them can be restored (a lesson I learned quite well from the mess in Strife's savegame code ;) ------------------------------------------------------------------------------- 12/23/10 Merged in a demo compatibility fix from trunk, at great inconvenience. It's going to be necessary to avoid any further code changes to trunk, since SVN cannot properly merge renamed files. ------------------------------------------------------------------------------- 12/18/10 The thinker factory system for manufacturing new thinker descendant instances during savegame loading is now complete. A CThinkerType base class maintains a static-global list head pointer to which all derived classes add a singleton instance at startup (order is unimportant). When the savegame code wants to unarchive a thinker, it will first read the archived class name (which is archived by CThinker::serialize when in write mode using the virtual getClassName() method), and it will then call the static FindType method of CThinkerType. This method will return a CThinkerType descendant object specialized to construct the same type of Thinker descendant which it names. Because all thinker type classes are essentially the same except for the name and the constructor they call (which are equal except one being a compile-time type and the other being a string literal), CThinkerType specializations are all defined using a macro, IMPLEMENT_THINKER_TYPE. This macro should and must be used once per thinker descendant. I am planning to place these macro definitions near the classes' Think method. ------------------------------------------------------------------------------- 12/16/10 Metatables are now zone allocatable, but a global solution for this which can be applied to any object is desperately needed before any widespread conversion of Eternity to C++ beyond the mere basics is undertaken. I restored the key and type parameters to the getNext* methods of MetaTable, as it turns out these were needed for something after all - they can in fact be NULL when starting a new search using these methods, in which case trying to get the key and type out of the object parameter itself would have caused quite the ugly access violation. I renamed copyTable to copyTableTo, and added a matching convenience method copyTableFrom, so that the directionality of MetaTable copy operations is both more optional, and bidirectional with minimal trouble. I am now refactoring all the MetaTable client code, which includes some significant portions of EDF. With this complete, we'll be back to only savegame-related errors. ------------------------------------------------------------------------------- 12/15/10 Yet more unnecessary #includes inside headers have been removed. These weren't causing any problem other than cluttering up the source code and slowing down compiles, but this is still a good improvement. I have begun to resolve the nightmare of a situation that is r_defs.h, first by splitting off a new header r_lighting.h which contains all #defines, typedefs, and structures related to lighttable_t and light fading. These were a particular problem, one which had earlier been hacked at grotesquely by conditionally duplicating the definitions between multiple header files and disabling duplicates by surrounding them inside their own #ifndef guards. Terrible! I changed all the Emacs mode selects *back* to C++, so that automated language detectors on services like ohloh will detect Eternity's change to C++ properly even if they pay too much attention to such indicators. ------------------------------------------------------------------------------- 12/13/10 Rewrote the critial MetaAPI. MetaObject is now the polymorphic base class for all descendant object types, and hashing and the management of MetaObject specializations for basic types such as int, double, and string have been retained in the MetaTable class. The MetaTable class's dual EHashTable structures have been moved into a "pimpl," or private implementation object, in order to isolate the rest of the metatable-using code from EHashTable, which is a relatively expensive-to- include template. Metatypes no longer exist at all, their functionality having been usurped entirely by the built-in virtual function calls of C++. Metaobject RTTI *has* been retained however, since C++'s language RTTI mechanism is not portable with respect to class names, and these may prove critical later on when Aeon scripting is designed to interact with metatables. The CopyTable method of MetaTable has been reimplemented in terms of a rather standard C++ cloning idiom, whereby a virtual clone() method in MetaObject acts as a virtual constructor. All MetaObject instances are required to override clone() and return an instance of their own type by calling their own copy constructor on *this. Note that covariant return types have NOT been used, to avoid problems with compilers that don't support them properly. ------------------------------------------------------------------------------- 12/12/10 Finished rewrite of the ehash_t structure into EHashTable, which is a template class which uses pointers to members so that it can hash types using any predetermined key field and set of CDLListItem list links, *without* imposing any sort of inheritance requirements onto the objects it supports, making it a pure container type. ------------------------------------------------------------------------------- 12/10/10 As part of the continuing effort to replace all type punning with proper inheritance, and because I may or may not need this code for supporting thinker serialization, I have turned to work on the m_dllist, e_hash, and metaapi modules. mdllistitem_t has been converted into a POD template class called CDLListItem. remove and insert methods are supported on the link type, and list head pointers should now be of type CDLListItem * rather than of the base type (this was a serious problem with the original design anyway, as it required weird punning of a pointer-to-pointer that always felt sinful). ------------------------------------------------------------------------------- 12/08/10 I've continued with significant work on the buffered IO classes and the rewrite of the savegame code, which is the primary remaining obstacle to compilation. Thinker serialization support is underway, but is unfinished. ------------------------------------------------------------------------------- 11/27/10 I have added a virtual enumeration mechanism to CThinker and an implementation of it in mobj_t to support twizzling of mutual reference pointers in savegames in a more encapsulated manner. Needs refinement - I may end up moving it back up the hierarchy into the thinker class, but time will tell. I realized I needed to check against "removed" status for all thinkers when downcasting them to specific types in loops (such as loops that only run on all valid mobj_t's) because under the old system, a thinker was no longer considered to be of the same type any longer once its thinker.function member had been changed to P_RemoveThinkerDeferred. Since Think is now a virtual method, it is unfortunately static at runtime and cannot be used for RTTI. To remedy this, I have created a thinker_cast<> template function which tests for removed status before performing a dynamic_cast. This removes any necessity to test for removed status all over the code, something definitely to be avoided at all costs, as forgetting to do it would be a costly mistake indeed. ------------------------------------------------------------------------------- 11/26/10 More #include cleanup has become necessary, but it should hopefully be finished for the time being. The buffered output functionality in m_buffer.cpp has been converted to a C++ class, which I hope to rederive from a generic class and then create a read version of the code to support buffered streaming of savegame data. (Later): Added CBufferedFileBase, rederived COutBuffer, and added CInBuffer. EE now has object-oriented buffered file IO facilities. ------------------------------------------------------------------------------- 11/22/10 Attempting to make changes to degenmobj_t to change it into a PointThinker base class from which Mobj and QuakeThinker can inherit has caused the entire codebase to suffer a circular include dependency hell problem that I have been predicting and fully expecting to occur for a couple of years now. The chief problem is that headers are including other headers, and then modules are relying on this in such a fashion that they're not properly including all of their dependencies independently. This was partially to blame on C's somewhat lacking abilities for foward type declarations, but also upon conscious decisions made by both id and especially, later, the BOOM team. Chief amongst offenders, and key in fact to the current meltdown, is the header r_defs.h - this massive conglomerate of unrelated structure types is an absolute nightmare which is either directly or indirectly included into virtually every single source module in Eternity - and it itself includes several other headers which, if followed in a tree-like fashion, end up including most of the code in the engine. This means that any modification ends up recompiling the entire codebase, and any change - such as innocently moving degenmobj_t to p_mobj.h - causes a nuclear meltdown. I have spent significant time to resolve this, chiefly by removing inclusion of headers within other headers wherever C++ allows this to be avoided simply by including forward declarations of types that are referenced only by pointer, for example: class mobj_t; struct line_t; struct sector_t; These three lines can take the place of 8000 lines of header-included code. With this resolved, work has continued on conversion of thinker_t's extended family of structs into a proper virtual class hierarchy. P_InitThinkers has become a static method of CThinker, and P_RemoveThinker has become a virtual Remove method. CPointThinkers contained in sectors, lines, and polyobjects will need to be temporarily constructed using placement-new, to guarantee that they contain valid vtable pointers and avoid possible undefined behavior with dynamic_cast which will be employed in the sound code when testing for sound origins that are really mobj_t's. If lines/sectors ever become non-POD types, then this will not be necessary any longer, as they could then call the CPointThinker constructor on their sound origin items directly. ------------------------------------------------------------------------------- 11/20/10 As noted in trunk, cpp-branch has been split off from trunk at r1344 to begin work on conversion of Eternity's codebase into the C++ language, so that we can retain our object-oriented designs without resort to ridiculous constructs such as unions of multiple structures, which seem to be the best suggestion from the neckbeards who wrote the C99 standard without regard to the Petabytes of perfectly valid C code they were backhandedly deprecating. The process is already well underway with the following series of changes: * All files have been renamed to .cpp, excepting fraggle's textlib code which will remain C (with likely need to add extern "C" blocks to its headers). * VC2008 project has been updated to link to the cpp files. * Elimination of all errors, including incorrect C++ usage of bool, pointer casts, and enum types, excepting error C2440, "no conversion from 'void (__cdecl *)(mobj_t *)' to 'think_t'" -- it is obvious that the thinker type punning system cannot be transitioned into C++, at least not in a clean manner, and thus as I had previously planned, transitioning thinkers into a virtual class hierarchy will be a fundamental part of the basic transition process. =============================================================================== EOF ===============================================================================