alephmodular-devel Mailing List for AlephModular (Page 14)
Status: Pre-Alpha
Brought to you by:
brefin
You can subscribe to this list here.
2002 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(61) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2003 |
Jan
(193) |
Feb
(61) |
Mar
(55) |
Apr
(5) |
May
(3) |
Jun
(1) |
Jul
(3) |
Aug
(14) |
Sep
(19) |
Oct
(48) |
Nov
(6) |
Dec
(25) |
2004 |
Jan
(1) |
Feb
|
Mar
(2) |
Apr
(6) |
May
(1) |
Jun
(1) |
Jul
(1) |
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
2005 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(17) |
Jun
(20) |
Jul
(13) |
Aug
|
Sep
|
Oct
(1) |
Nov
|
Dec
|
2008 |
Jan
|
Feb
|
Mar
|
Apr
(2) |
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Br'fin <br...@ma...> - 2003-01-30 04:44:27
|
I've been mucking with a spare copy of AM, actually trying to implement what I have described in my draft so far. Which is of course turning up more areas to work upon. Like, most files are going to be opened in binary mode. So this makes a reasonable default, however, some files in the future are going to be text based, so we should support that as well. I was thinking of exposing the iostream openmode flags as part of the open_file_for_reading/writing business. And just have it default to binary. You could clear it of course by passing 0 for the mode (or a suitably equivalent definition) The other two aspects I found myself leaning towards were calling conventions and error feedback. By calling conventions I was thinking of calls like this: virtual bool get_named_child(const char *name, IFileSystemDesc* &out_desc) const = 0; This is a method to specify a file or directory within a directory. The name of that desc would be the string name. If the function was successful, then it would return true, and the IFileSystemDesc* would point to a newly acquired class. (Which the caller would need to free currently) If the function failed, then it would return false, and the IFileSystemDesc* would remain untouched. And the reason for failure? Well, after a little bit of thinking, I realized that there should be an IError interface with three methods. A protected set_state, a public get_state and a public clear_state. And in thinking of it, it's possible that the state itself could be verbose enough to display/log when dumped to a stream. Why not exceptions? I have not been sold on exception handling in C++ under some warnings of exception handling being there, but less than optimal under C++ (Less practical in terms of speed, doing extra work with stuff too low level for me really to be thinking about :) ) and a simple lack of knowledge on how one is supposed to do it in C++. (Java is at least clear on what things you should be throwing around) Admittedly a nice clear document that counters these perceptions as myth might change my mind. -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-01-30 03:19:15
|
On Wednesday, January 29, 2003, at 11:26 AM, Woody Zenfell, III wrote: > On Monday, January 27, 2003, at 07:59 PM, Br'fin wrote: > >> <class file>.h >> <class file>.cpp >> <class file>_<platform>.cpp >> > > Don't forget <class file>_<platform>.h; different platforms could > provide different interfaces (probably extended in one way or another) > and various bits of code may want to get at those extensions (after > checking their availability, perhaps with dynamic_cast<> e.g.). D'oh, I forgot about that file. *and* I was planning on using such features. :) And yes, it's ok for a mac platform to dynamic_cast the file objects to another likely state. > Naturally the 'main interface' should cover as much as possible, but > that doesn't necessarily mean it would be wise to restrict all uses to > only that interface. > > Actually hmm, how exactly should this work out? > > // Class.h > class IClass { all pure virtual, virtual destructor }; > > // Class.cpp > (do-nothing destructor) > > // Class_Shared.h > class CClass_Shared : public IClass { methods that most folks will be > able to use regardless of platform }; > > // Class_Shared.cpp > (implementations for those) > > // Class_Carbon.h > class CClass_Carbon : public CClass_Shared { platform-specific > methods, including perhaps overrides of some in CClass_Shared }; > > // Class_Carbon.cpp > (implementations for those) I'm hazy on creating a pure interface versus a base one. I'm jut not experienced enough to know which way to lean when it's not obvious. With a pure interface though, there's nothing to destruct with that virtual destructor. So Class.cpp is unneeded in the situation listed above. My own thinking was to have what you have listed as Class_Shared pushed into Class.h/Class.cpp. If a sub class needs a major overhaul, well the interface is there to extend upon, overriding everything. And if it really can't use the common stuff, then perhaps we built things wrong. Agreed on the platform specific files. > Of course whenever practical, CClass_Shared should target a > platform-neutral interface, and accept one or more platform-specific > helper objects that implement that interface (perhaps requested from a > Factory) to do its work, to help avoid 'combinatorial explosion'. But > I probably didn't need to state that. > I'm not sure what you mean by 'combinatorial explosion'. (It looks like a bunch of those cases where you say 'I don't need to expound on this' you actually do, if just because I'm dense) Also not sure of the difference between a subclass that does foo gotten from a factory and a class that does foo with the aid of a subclass gotten from a factory. Unless you mean it allows other classes to go new MYClass and it automatically starts delegating to an internal platform object derived from a factory... -Jeremy Parsons |
From: Woody Z. I. <woo...@sb...> - 2003-01-29 16:27:18
|
On Monday, January 27, 2003, at 07:59 PM, Br'fin wrote: > <class file>.h > <class file>.cpp > <class file>_<platform>.cpp > Don't forget <class file>_<platform>.h; different platforms could provide different interfaces (probably extended in one way or another) and various bits of code may want to get at those extensions (after checking their availability, perhaps with dynamic_cast<> e.g.). Naturally the 'main interface' should cover as much as possible, but that doesn't necessarily mean it would be wise to restrict all uses to only that interface. Actually hmm, how exactly should this work out? // Class.h class IClass { all pure virtual, virtual destructor }; // Class.cpp (do-nothing destructor) // Class_Shared.h class CClass_Shared : public IClass { methods that most folks will be able to use regardless of platform }; // Class_Shared.cpp (implementations for those) // Class_Carbon.h class CClass_Carbon : public CClass_Shared { platform-specific methods, including perhaps overrides of some in CClass_Shared }; // Class_Carbon.cpp (implementations for those) This way a platform that does things radically different or something can use only the interface, and not inherit any of the default behavior of the shared class. OTOH platforms that have a lot in common can share that common code. ("Template Method", anyone?) Perhaps Class_Shared should have a more descriptive identifier than "Shared", that indicates something about its implementation approach - so that there can be other shared superclasses later that take a different approach. Of course whenever practical, CClass_Shared should target a platform-neutral interface, and accept one or more platform-specific helper objects that implement that interface (perhaps requested from a Factory) to do its work, to help avoid 'combinatorial explosion'. But I probably didn't need to state that. Woody |
From: Br'fin <br...@ma...> - 2003-01-29 15:49:40
|
The is_a hierarchy was my attempt to link related types. You yourself commented on it previously as well. For instance, you're trying to build up the shapes menu and that part of the code shouldn't care what format the shape file is in. It shouldn't care that it's a M2 file or an M1 file. So I'm trying to address that issue. And I also got to wondering, if you could say something is a map file, shouldn't you also be able to say whether its a Marathon2 file or not? So far I'm mixed between trying to come up with a class hierarchy or an attribute based hierarchy. And with the addition of file type and originator coming into play, I'm beginning to lean towards an attribute based one. -Jeremy Parsons On Wednesday, January 29, 2003, at 10:36 AM, Woody Zenfell, III wrote: > Now it's my turn to not follow you so well. :) > > Perhaps you could illuminate your thinking with a more specific > example? (e.g., what's in the Marathon2Type interface? What's in the > MapType interface? Explain about the is_a hierarchy.) > > Woody > > > On Wednesday, January 29, 2003, at 09:07 AM, Br'fin wrote: > >> On multiply typed elements >> After a quick bit of thinking, do we have need for types with >> multiple attributes? And how should that be handled? For instance, a >> CMarathon2MapType is both a CMapType and a CMarathon2Type. I take >> this to mean two possible implementations: >> >> 1) CMarathon2MapType is a direct subclass of CFileType and implements >> IMapType and IMarathon2Type interfaces. >> >> 2) The is_a hierarchy is maintained in a different way. Like real >> attributes on the object. >> >> I admit, on typing I'm trying to figure out if the class is >> C<whatever>Type and is essentially a singleton. What is a decent way >> of referring to one specific type or another? >> >> A CFileTypeFactory that offers GetTypeFor("<whatever>") with a hash >> or dictionary within? >> >> -Jeremy Parsons >> |
From: Woody Z. I. <woo...@sb...> - 2003-01-29 15:36:51
|
Now it's my turn to not follow you so well. :) Perhaps you could illuminate your thinking with a more specific example? (e.g., what's in the Marathon2Type interface? What's in the MapType interface? Explain about the is_a hierarchy.) Woody On Wednesday, January 29, 2003, at 09:07 AM, Br'fin wrote: > On multiply typed elements > After a quick bit of thinking, do we have need for types with multiple > attributes? And how should that be handled? For instance, a > CMarathon2MapType is both a CMapType and a CMarathon2Type. I take this > to mean two possible implementations: > > 1) CMarathon2MapType is a direct subclass of CFileType and implements > IMapType and IMarathon2Type interfaces. > > 2) The is_a hierarchy is maintained in a different way. Like real > attributes on the object. > > I admit, on typing I'm trying to figure out if the class is > C<whatever>Type and is essentially a singleton. What is a decent way of > referring to one specific type or another? > > A CFileTypeFactory that offers GetTypeFor("<whatever>") with a hash or > dictionary within? > > -Jeremy Parsons > > > > ------------------------------------------------------- > This SF.NET email is sponsored by: > SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See! > http://www.vasoftware.com > _______________________________________________ > Alephmodular-devel mailing list > Ale...@li... > https://lists.sourceforge.net/lists/listinfo/alephmodular-devel > |
From: Br'fin <br...@ma...> - 2003-01-29 15:07:15
|
On multiply typed elements After a quick bit of thinking, do we have need for types with multiple attributes? And how should that be handled? For instance, a CMarathon2MapType is both a CMapType and a CMarathon2Type. I take this to mean two possible implementations: 1) CMarathon2MapType is a direct subclass of CFileType and implements IMapType and IMarathon2Type interfaces. 2) The is_a hierarchy is maintained in a different way. Like real attributes on the object. I admit, on typing I'm trying to figure out if the class is C<whatever>Type and is essentially a singleton. What is a decent way of referring to one specific type or another? A CFileTypeFactory that offers GetTypeFor("<whatever>") with a hash or dictionary within? -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-01-28 01:58:56
|
I was just working over the abstract file document. And decided to add a part about implementation details. A quick note about files, and how they will be migrated. (so in the case below, going back in CFiles.h far enough would reveal it was once portable_files.h) > File organization details: > Initial work will proceed in portable_files.h, files_macintosh.cpp and > Support/CFiles.cpp. At a later point in time when files are > reorganized, this will become > Support/CFiles.h > Support/CFiles.cpp > Support/CFiles_macintosh.cpp In other words <class file>.h <class file>.cpp <class file>_<platform>.cpp -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-01-27 23:31:56
|
On Monday, January 27, 2003, at 12:02 PM, Woody Zenfell, III wrote: > > 2. Just a reminder that there ought to be a way to enumerate the > entries (probably optionally matching a type) in a directory. (This > probably will be later fleshed out in the "stub interface for now" for > directory desc's.) Yeah, I skimmed over that. I accept that there should be a way to interface with directories, But since my focus has been on the files I rather blanked on what functions we need from a directory. > 3. Should the Unknown file type really never match anything, > including Unknown file type? I understand the reasoning - if you > can't tell what type it is, you surely can't tell whether it's the > same as some other type you don't recognize - but I could see it being > useful in certain circumstances to be able to, say, enumerate all the > files in a directory that *aren't* recognized as some other type. > Maybe this uses a special enumeration call rather than the Unknown > type designator, etc. Shrug. By matches I was referring to the way we try to determine type from CFileSystemDesc. The is_a check should still say 'Yes we have an unknown file type.' Initially we get a file desc but have no clue what it is. So we loop through our type tests. All of our type tests fail so we give up and give the description the CUnknownType. > 4. Perhaps there should be a distinction drawn in the interface > between "looks like" a type (quick to determine from extension/Type > Code/etc.) and "really is" a type (requires examining the file > contents to be sure). I know you proposed a 'toggle', which might > work out, but having this explicitly exposed in the interface will > make programmers think a little more about what they're really working > with (which may be particularly good for files that Look Like, say, > Shapes or something, but Really Are, like, umm, a film file). My assumption with the toggle and all was that this toggle would be pretty much encapsulated in the type checking facility. Then again my reasoning is 'if it claims to be a shapes file (shp2) treat it as such, and of course report 'bad shapes file' if it betrays us by being a film' and the full out peek into the file check was for cases where the first checks fail. We have a random file 'foo', no type, no extension. Is it anything we think we recognize? > 5. Should types be hierarchical? (Is this already implicit in your > design? sorry...) That is, I mean you talked about a general "Shapes > file" identifier that encompasses several specific Shapes-file > representations I think, but I got the impression there was a strict > two-level hierarchy there, probably with the general identifiers and > the specific identifiers in two separate matching spaces. Instead, > should there be a more general, potentially multi-level matching > mechanism that's a bit like, perhaps, class inheritance? So one can > ask for things matching some more general type and get things of > various specific types that are subtypes of the general type? I think I brought up the concept of hierarchy, but brushed over it poorly. But I was thinking along those lines. You have specific file types for M1 Map, M2 Map, Minf Map, etc. But if all you care about is that it's a Map file, then you'd be set by specifying the Map type that all those other map types inherit from. > Of course hmm kind of harkening back to some of my earlier notes in > this regard, I'd advocate hiding the distinction between a Shapes file > and a Sounds file etc. as much as possible, and treating all such > files simply as 'archives of objects'. An m2-format Shapes file > happens to provide only objects of type M2 High-Level Shape, M2 > Low-Level Shape, M2 Bitmap, M2 CLUT, etc. (whatever's in there), but > in the future potentially some single new-format archive file could > contain Shapes information, levels, terminals, etc. all together. > This means that instead of caring about what type a file *is* (except > for finding the appropriate file-decoder to use), we care about what > kinds of objects (and there could be many, per file) a file > *provides*. My own thought, and this is, alas, very general at the moment. Is to aim for a bundle type of structure. A folder with a well defined location for scenario information. (Required Bio, descriptions of other resources needed or provided) The Environment preferences wouldn't go away, but would be overhauled. Something along the lines of Choose Scenario (With options like Custom, ---, M1, M2, Minf, Devil in a Blue Dress) With a the custom option being a pane similar to the current one: Choose Bio, physics, shapes, sounds, map. This isn't exactly an uber-file though and many of the subcomponents can be handled with the existing file readers. > So, if we play an M2 Level, it refers (thanks to the M2 Level Unpacker > object) to the monster type M2 Trooper, which the engine finds in a > 'bio' somewhere (possibly compiled-in, possibly not). The bio > (right?) refers to the M2 Trooper Shape information etc., which the > engine locates one way or another in some M2-format Shapes file. The > user chooses only the level (possibly implicitly, by starting to play > a scenario, or by linkage from another level); the level determines > the Bio; the Bio determines the other objects needed. The user never > deals with any of this "Environment" nonsense (though there ought to > be a mechanism for optionally explicitly overriding one thing with > another, so that the user can choose to play M1's Waldo World Arena > with all M1 standard stuff but using his own "Jetpack" physics model > instead of the normal M1 physics model - or M1 Waldo World Arena with > M1 weapons and wall textures etc. etc. but with all major Pfhor > replaced with Moo Hunters, or whatever... this latter would probably > require some sort of configuration file be created and specified; I > wouldn't expect to be able to set up a situation this complex in the > game's UI). Note that this approach allows for future (significant!) > expandability/flexibility without compromising support for existing > files as-is. I agree that we want to hit this kind of target functionality. Unsure on the full flexibility with respect to the scenario with the configuration file. Though if we do allow multiple shapes files (either adding to or overlaying aspects of an existing one) then yes, that seems better controlled in the scenario setup file. > The M2 Shapes loader (etc.) could be told what names to use for the > objects it finds by an additional external naming file. This way > existing scenarios could use their standard data files as-is, but > could provide differently-named objects by way of these drop-in add-on > files. (Perhaps these naming files could specify which files they > apply to by checksum etc. so that there could be a standard > distribution of them, covering all existing scenarios that folks have > bothered to create naming information for - but without of course > requiring that the user actually *have* all the scenarios already. > This way users can download and use existing scenarios - in their > existing distributions - but AM can still benefit from the generalized > referencing.) I'm not quite following this, I fear. > Yeah I'm going to keep advocating this 'runtime dynamic linking of > game objects' model because I feel strongly that it's The Right One. > :) At least, until it's sufficiently refuted or a stalemate appears > (as in the SDL-as-baseline-platform debate). Currently I'm mixed. Like in the rendering engine. Is it better to support drawing multiple forms of shapes at draw time or would it be better to pick one format, work with that and reconvert any other shapes on at load time for compatibility? Most explicitly, we pick M2 shapes as the way a sprite should be encoded. Hence, when working with M1 shapes, we need to read the M1 shape in then convert it, in memory, to the M2 shape structure before we continue loading the level. Other areas, like children of Monster that manage monster AI in the game, I definitely agree with having dynamic runtime classes. > While we're at it, should we talk about the possibility of keeping > some sort of table of properties about files? Like a quick index of > what we've determined various files to Really Be, a unique identifier > for the file contents a la the 'checksum's role in Bungie's > formulation, to locate the correct file (for film playback, netgame, > etc.) even if its name has changed, what objects the file provides, > etc.? Ehh, probably not, these operations are probably used > infrequently enough that it's not worth keeping a separate table. > > Well hmm I mean, I had pictured storing this table on disk, which is > probably a bit unnecessary, but OTOH caching/memoizing the results of > operations like Really Is in memory (during a single run of the > application) may be appropriate...? Not that this has to be addressed > now of course, just thought I'd mention it before it left my brain > again. > I could see going with some kind of in memory list of properties about files. I haven't really decided, but there is room for this, especially with my edict about CFileSystemSpecs... to navigate you don't edit an existing one in place, instead you use it to navigate to a newly allocated one. Right now things are just being ditched instead of being stored, though. -Jeremy Parsons |
From: Woody Z. I. <woo...@sb...> - 2003-01-27 17:10:23
|
On Monday, January 27, 2003, at 10:02 AM, Br'fin wrote: > Ok, I've done some more changes and been trying to think more things > through. Here's my updated design document for the file abstraction. :) Five comments... and then some standard me-style tangential increasingly-off-topic blathering. 1. First off, well done! This is looking like some pretty good stuff to me. 2. Just a reminder that there ought to be a way to enumerate the entries (probably optionally matching a type) in a directory. (This probably will be later fleshed out in the "stub interface for now" for directory desc's.) 3. Should the Unknown file type really never match anything, including Unknown file type? I understand the reasoning - if you can't tell what type it is, you surely can't tell whether it's the same as some other type you don't recognize - but I could see it being useful in certain circumstances to be able to, say, enumerate all the files in a directory that *aren't* recognized as some other type. Maybe this uses a special enumeration call rather than the Unknown type designator, etc. Shrug. 4. Perhaps there should be a distinction drawn in the interface between "looks like" a type (quick to determine from extension/Type Code/etc.) and "really is" a type (requires examining the file contents to be sure). I know you proposed a 'toggle', which might work out, but having this explicitly exposed in the interface will make programmers think a little more about what they're really working with (which may be particularly good for files that Look Like, say, Shapes or something, but Really Are, like, umm, a film file). 5. Should types be hierarchical? (Is this already implicit in your design? sorry...) That is, I mean you talked about a general "Shapes file" identifier that encompasses several specific Shapes-file representations I think, but I got the impression there was a strict two-level hierarchy there, probably with the general identifiers and the specific identifiers in two separate matching spaces. Instead, should there be a more general, potentially multi-level matching mechanism that's a bit like, perhaps, class inheritance? So one can ask for things matching some more general type and get things of various specific types that are subtypes of the general type? Of course hmm kind of harkening back to some of my earlier notes in this regard, I'd advocate hiding the distinction between a Shapes file and a Sounds file etc. as much as possible, and treating all such files simply as 'archives of objects'. An m2-format Shapes file happens to provide only objects of type M2 High-Level Shape, M2 Low-Level Shape, M2 Bitmap, M2 CLUT, etc. (whatever's in there), but in the future potentially some single new-format archive file could contain Shapes information, levels, terminals, etc. all together. This means that instead of caring about what type a file *is* (except for finding the appropriate file-decoder to use), we care about what kinds of objects (and there could be many, per file) a file *provides*. So, if we play an M2 Level, it refers (thanks to the M2 Level Unpacker object) to the monster type M2 Trooper, which the engine finds in a 'bio' somewhere (possibly compiled-in, possibly not). The bio (right?) refers to the M2 Trooper Shape information etc., which the engine locates one way or another in some M2-format Shapes file. The user chooses only the level (possibly implicitly, by starting to play a scenario, or by linkage from another level); the level determines the Bio; the Bio determines the other objects needed. The user never deals with any of this "Environment" nonsense (though there ought to be a mechanism for optionally explicitly overriding one thing with another, so that the user can choose to play M1's Waldo World Arena with all M1 standard stuff but using his own "Jetpack" physics model instead of the normal M1 physics model - or M1 Waldo World Arena with M1 weapons and wall textures etc. etc. but with all major Pfhor replaced with Moo Hunters, or whatever... this latter would probably require some sort of configuration file be created and specified; I wouldn't expect to be able to set up a situation this complex in the game's UI). Note that this approach allows for future (significant!) expandability/flexibility without compromising support for existing files as-is. The M2 Shapes loader (etc.) could be told what names to use for the objects it finds by an additional external naming file. This way existing scenarios could use their standard data files as-is, but could provide differently-named objects by way of these drop-in add-on files. (Perhaps these naming files could specify which files they apply to by checksum etc. so that there could be a standard distribution of them, covering all existing scenarios that folks have bothered to create naming information for - but without of course requiring that the user actually *have* all the scenarios already. This way users can download and use existing scenarios - in their existing distributions - but AM can still benefit from the generalized referencing.) Yeah I'm going to keep advocating this 'runtime dynamic linking of game objects' model because I feel strongly that it's The Right One. :) At least, until it's sufficiently refuted or a stalemate appears (as in the SDL-as-baseline-platform debate). While we're at it, should we talk about the possibility of keeping some sort of table of properties about files? Like a quick index of what we've determined various files to Really Be, a unique identifier for the file contents a la the 'checksum's role in Bungie's formulation, to locate the correct file (for film playback, netgame, etc.) even if its name has changed, what objects the file provides, etc.? Ehh, probably not, these operations are probably used infrequently enough that it's not worth keeping a separate table. Well hmm I mean, I had pictured storing this table on disk, which is probably a bit unnecessary, but OTOH caching/memoizing the results of operations like Really Is in memory (during a single run of the application) may be appropriate...? Not that this has to be addressed now of course, just thought I'd mention it before it left my brain again. Woody |
From: Br'fin <br...@ma...> - 2003-01-27 16:02:43
|
Ok, I've done some more changes and been trying to think more things through. Here's my updated design document for the file abstraction. :) -Jeremy Parsons --- Technical Specification: File Hardware Abstraction First there was portable_files.h and it was good in concept. But there was only a Macintosh implementation. And even so, there was an evident bios in the structure towards the Macintosh. Please note that this does not handle file browsing. That is in the realm of the GUI. Platform specific stuff can use the platform specific CFileSystemDesc constructor. IE Mac would support CMacFileDesc(FSSpec&) CFileSystemDescFactory is a singleton class that provides access to a set of base CFileSystemDesc locations. But in order to navigate, you can't use just create a CFileSystemDesc(...) You would need to use a factory to get an initial CFileSystemDesc. From there you could navigate. The factory is also the most reasonable place to provide access to CFileSystemDescs for specific platform specific places. Ala preferences directory and application directory CFileSystemDesc *foo = FILE_DESC_FACTORY- >GetDescFor(CFileSystemDescFactory::ApplicationDirectory); // It is an error to delete this foo Specific directories so far are ApplicationDirectory - The directory the application has been run from (Current dir if command line, directory of application executable otherwise) PreferencesDirectory SavedGameDirectory ReplayDirectory ResourcesDirectory - Where to find platform generic support files CFileSystemDesc represents a file's location. Under Unix, this would be the full path to a file. Under Macintosh this is akin to a FSSpec. Most typically it refers to a parent directory and knows the name of its file within that directory. Most notably this has functions for creating the specified file, opening the file for reading, or opening the file for writing. The basic strategy for CFileSystemDesc is to create each CFileSystemDesc as a static entity. The process of navigating based on the CFileSystemDesc therefore doesn't modify the CFileSystemDesc, but instead creates new CFileSystemDescs A given platform specific derivative should accept platform native file specifications. (For interfacing with native file browsers) See CFileType for more about the type of the element Basic properties (Not necessarily public!) path that contains current element (/users/brefin/Documents) name of current element (readme.txt) type of current element (TEXT, txt) One of directory #define SCENARIO_FILE_TYPE 'sce2' #define SAVE_GAME_TYPE 'sga2' #define FILM_FILE_TYPE 'fil2' #define PHYSICS_FILE_TYPE 'phy2' #define SHAPES_FILE_TYPE 'shp2' #define SOUNDS_FILE_TYPE 'snd2' #define PATCH_FILE_TYPE 'pat2' Public Operations include exists is_file is_directory public file operations (IFileDesc) (These are errors if called on a directory) open_file_for_reading (Returns CFileRef) open_file_for_writing (Returns CFileRef) open_resource_file_for_reading (Returns CResourceFileRef) public directory operations (IDirectoryDesc) (These are errors if called on a file) Stub interface for now. Protected operations create_file iofname() - return an if/ofstream compatible path for the file swap(CFileSystemDesc) Safe Save support Swaps contents of two file descs These should be two files on the same device and use OS file swapping calls where available delete_file - accessible by friend CFileRef Navigation of CFileSystemDescs would include methods to work based on the CFileSystemDesc ala CFileSystemDesc GetParent() to get the parent of this directory and CFileSystemDesc GetNamedChild(itemname) GetTemporaryCopy Returns a CFileSystemDesc to a temporary file in the same directory as the current CFileSystemDesc, the name should be unique and the file shouldn't already exist. CFileRef represents an open file Operations include file operations are by us being either an ifstream or an ofstream // get_fpos // set_fpos // set_eof // get_file_length // read_file // write_file close_file is implicit in deleting the CFileRef. Hence attempts to fclose(CFileRef) should create a compile time error if possible. A note on saving files. For a given FSSpec you may only have one file open for writing. In addition, when you open a file for writing the following transpires. You are given an ofstream derived object that is internally associated with the current CFileSystemDesc and a temporary one. The temporary CFileSystemDesc is guaranteed to be in the same directory as CFileSystemDesc and to have a unique name. Upon proper closure of the stream the temporary file the following happens. If CFileSystemDesc exists, than the temporary file is swapped with it and the original content of CFileSystemDesc discard (swap occurs, then temporary file deleted) Otherwise the temporary file is renamed to the name in CFileSystemDesc. ofstream.close() isn't virtual, so we can't override it, but it does have a virtual destructor. Within there we can do: this->close() if(this->is_good()) // The closing of the file didn't raise any errors { curFileDesc.swap(tempFileDesc); tempFileDesc.delete(); } IFileType represents the type of a file CFileSystemRef is our friend and can use the protected virtual methods for getting type_code, type_extension and the type checking. There are two special cases of IFileType CDirectoryType for directories CUnknownType for CFileSystemDescs that haven't been examined yet All others descend from CFileType which has basic operations A file type generally encapsulates the following: The four character code associated with a file on Macs: aka TEXT The extension associated with the file on other systems: aka txt (as in README.txt) a method of checking whether or not a CFileSystemDesc matches the current type and returns a boolean (grr, I can't think of a reasonable name for this method... is_this_yours, matches_type, type_check, check_type...) CDirectoryType only matches type with directories CUnknownType never matches. But is only assigned when either nothing is known, or everything else fails to match. At any rate, the method for checking type can be expanded upon. It doesn't just have to go by type and/or extension. It is allowed to open and inspect the file for more info. However, there should be a toggle. Thus letting the system do a quick first pass checking type/extension. And then, if the file is still unknown, do the longer file inspection checks. FileTypes should also support an is_a operator. For instance, all map files, no matter what type or version, should return true for is_a(CMapType) Even though it would be an error to set any given file's type explicitly to CMapType. |
From: Br'fin <br...@ma...> - 2003-01-27 04:33:34
|
On Sunday, January 26, 2003, at 08:54 PM, Woody Zenfell, III wrote: > On Sunday, January 26, 2003, at 04:10 PM, Br'fin wrote: > >> On Sunday, January 26, 2003, at 01:08 PM, Woody Zenfell, III wrote: >> >>> Right, Tim is right on, sorry I thought it was pretty clear how it >>> would work out. >> >> I know I'm being a pest about it. But no one (myself included) seems >> to have explicitly jotted anything down in terms of mixed file >> support Replays are just one case. > > ... but now you're satisfied? :) Doubtful, but we're making progress. :) >> Much of my reasoning for compact and concise is that the action flags >> were implemented initially as flags on a 32bit word. And I would >> think they were implemented that way for a reason. AM is probably >> going to be a touch thicker and slower than the original code design. >> But I would prefer more of the processing to go towards the rendering >> and gameworld updating of things. Please read on by the way. > > I think they were originally implemented that way because > > 1) Bungie knew they could fit everything they wanted to into a 32-bit > value > 2) Copying 32-bit values is easy > 3) So why take more? > > I'd tend to think that we don't know how much storage someone using AM > will want for their ActionFlags at some arbitrary point in the future. > So it makes sense to me to completely hide the storage inside an > object. That part is fair enough to me. Right now I don't think there's even a typdef for passing the flags around, it's just an int32. >> First, we don't have one person taking AM in one direction and >> someone else taking AM in a second direction. >> >> Secondly, they are more than welcome to fork AM and create their own >> project tailored for their game and needs. If one project has feature >> A and another has feature B. Neither necessarily has to be folded >> into the main codebase. > > So custom scenarios are generally going to have to provide a custom > build for every AM platform? And scenarios won't be able to take > advantage of later AM enhancements because they've broken away from > the trunk? Well, at the point where they are adding custom flags you have someone who needs the inputs and knows how to add the corresponding control code to the other end of the game in the game core. You also have someone who is doing significantly more work than 'just another scenario'. Since scripting isn't even on our table yet (too many other things going on) there is no way to utilize new flags without compiling support into the game. I want to support this kind of beyond the scenario development. But we need to recognize it as that as well. They've forked the project to build off of and develop their game. It is up to them to keep track of further AM development and fold it into their version of the project should it fit their needs. >> AM, is, alas, C++. To my knowledge C++ starts breaking down when you >> start trying to pass a subclass by value to a class expecting the >> master class. I think this is why Java implicitly treats every object >> as essentially a pointer to that object. >> >> I didn't know you could ask for a vector<I don't know what class to >> use until runtime>. > > You could compile in (via templates) support for various vector<> > instantiations, wrap them in subclasses of some superclass, and have a > factory dish out various subclass instances in the guise of a > superclass pointer. > > All that said, it may prove to be significantly more straightforward > _to_ use pointers. Shrug. Just pointing out that there is an option. That just sounds like it getting out of hand. :) >> I don't find the concept of dynamically displaying available control >> flags and tying controls to them a bad idea, just querying the >> ActionFlags itself to be unusual. Even in this soft code case I would >> lean towards either a separate class or the encoder (parse_keymap >> (and mice!) object) handling the exposition of 'there's flag >> _trigger_1 with name "Trigger 1" and it is a toggle-switch (No mouse >> or other analog input need apply)' > > Whereas it seems perfectly natural to me to think that nobody knows > better what an ActionFlags object is capable of representing than the > ActionFlags object (or its class - but the general lack of support for > runtime class stuff in C++ is quite a liability). How is an external > object going to know? Does someone have to match the correct external > object to the correct ActionFlags object? Why make them do that? I think part of it is just my point of view. I see there being two main components. The ActionFlags itself being a carrier of information (And being really dumb about what it carries, mind you) and the encoder of the information, the object that creates new ActionFlags based on the current input. The encoder in this view isn't any old external object, its tied to the Action Flags. The ActionFlags itself would be fairly dumb. Somewhere in a class shared by all action flags is this lookup table. Masked lengths and shift values for each index (flag) Actionflags flags; flags[_trigger_1] = 1; // 1 bit value flags[_absolute_positioning_yaw] = 1; // 1 bit value flags[_absolute_yaw_value] = 30; // 7 bit value With an implementation just for cramming values in. Another object (the ActionFlags configuration reader? The encoder?) would be responsible for building that lookup table and perhaps have more description information. Labels or tool tips for each field and the like. Things irrelevant to the flags itself, but useful for setting up the encoder or showing the user the options to control. >> I swear that seemed like the only Computer Course I took in school >> that defined why it was useful to know and I kick myself that I can't >> remember more of it. :) > > Believe it or not, I never had any classes on object-orientation... so > I didn't "get it" until much later, principally from the two sources > cited. :) Please try to take my comment about learning about OO in > this context, rather than as some kind of slight or something. (But I > think you did anyway.) > > Of course I still probably don't *really* "get it", I just think I do. > Sort of like before. But I get it better now than I did before. :) I think part of the reason why I'm kicking myself now is that I didn't really grasp the notions at the time. So I've been exposed to them, I know they're out there, I've read some of the materials, but am still wrestling with trying to understand the OOAD and apply it. >> I honestly don't know how many threads we may have going on. > > All the more reason to have separate queues for separate tasks. :) Heh, accepted. >> For decent Carbon control, I already feel a need to be able to invert >> the relation of input thread and rendering/game update thread. > > This is because, essentially, parse_keymap() cannot be implemented in > a way that will work in the non-main thread? Yeah, between the mouse and the issues with get_keys. Mouse polling latched to rendering speed kills all the smoothness when frames drop. > If the problem's principally with the mouse control, it might be > interesting to see how SDL (I mean SDL at least as much as A1/SDL) > does it. IIRC the A1 Carbon version had this problem where the > pointer could reach the end of the screen and would refuse to go > further... but the Mac OS X SDL version had no such problem. At the > least, in the Carbon version I'd end up starting Screen Effects a lot > which was not a problem in Mac OS X SDL A1. SDL might have already discovered such effects and might already have yet another thread or way of separating the carbon based input with where it's needed. Hmm, I don't even know which set of libs SDL is using. Might not be using much of Carbon anything at all... The screen effects stuff is more likely my fault. In A1/Carbon, the mouse is tracked via mousemoved and mousedragged events. Which have full deltas even if the absolute position of the mouse is crammed into the bottom corner of the screen. I do know how to warp the mouse position, but I don't think that's in the current A1 code. >> It's my impression with A1 that during network play, the usual input >> thread is skipped, instead relying on a network input thread. For >> Carbon would this then mean 3 threads going on? (rendering/update, >> Carbon input, network tasks) > > This is exactly right. In fact, there's a fourth task, but it doesn't > run all that often. > > In any circumstance, you at least have: > > Main thread - update/rendering > Periodic task - capture input (or, in film playback, just move it from > the recording_queues to the player_queues). In a netgame on the > server, this may also send out network packets. > > In netgames you also have: > Reactive task - runs code in response to packet arrival (often results > in packet send) > Periodic task - resend unacknowledged packet (in practice this is > usually reset - in response to getting an ack - before it gets a > chance to fire and run) Ahhh, ok then. -Jeremy Parsons |
From: Woody Z. I. <woo...@sb...> - 2003-01-27 01:54:34
|
On Sunday, January 26, 2003, at 04:10 PM, Br'fin wrote: > On Sunday, January 26, 2003, at 01:08 PM, Woody Zenfell, III wrote: > >>> Given the importance of this, I want more detail than this. This >>> cannot be a "magic happens here." I want to know *how* you determine >>> it to be what kind of film file it is. >> >> Right, Tim is right on, sorry I thought it was pretty clear how it >> would work out. > > I know I'm being a pest about it. But no one (myself included) seems > to have explicitly jotted anything down in terms of mixed file support > Replays are just one case. ... but now you're satisfied? :) > Much of my reasoning for compact and concise is that the action flags > were implemented initially as flags on a 32bit word. And I would think > they were implemented that way for a reason. AM is probably going to be > a touch thicker and slower than the original code design. But I would > prefer more of the processing to go towards the rendering and gameworld > updating of things. Please read on by the way. I think they were originally implemented that way because 1) Bungie knew they could fit everything they wanted to into a 32-bit value 2) Copying 32-bit values is easy 3) So why take more? I'd tend to think that we don't know how much storage someone using AM will want for their ActionFlags at some arbitrary point in the future. So it makes sense to me to completely hide the storage inside an object. > First, we don't have one person taking AM in one direction and someone > else taking AM in a second direction. > > Secondly, they are more than welcome to fork AM and create their own > project tailored for their game and needs. If one project has feature A > and another has feature B. Neither necessarily has to be folded into > the main codebase. So custom scenarios are generally going to have to provide a custom build for every AM platform? And scenarios won't be able to take advantage of later AM enhancements because they've broken away from the trunk? >> 1. Who ever said anything about storing pointers? I was picturing >> the queues etc. storing the ActionFlags objects directly in their >> chunks of memory, a la STL vector et al. This is possible since every >> ActionFlags object in use at a given moment is of the same type. > > AM, is, alas, C++. To my knowledge C++ starts breaking down when you > start trying to pass a subclass by value to a class expecting the > master class. I think this is why Java implicitly treats every object > as essentially a pointer to that object. > > I didn't know you could ask for a vector<I don't know what class to use > until runtime>. You could compile in (via templates) support for various vector<> instantiations, wrap them in subclasses of some superclass, and have a factory dish out various subclass instances in the guise of a superclass pointer. All that said, it may prove to be significantly more straightforward _to_ use pointers. Shrug. Just pointing out that there is an option. > Thirdly, my current impression is there's too much 'hard coding' in > your thinking of ActionFlags. (Again, continue reading, I have no idea > where to group my proposal around this) > By 'hard coding' I mean explicitly putting everything into the > ActionFlags or subclasses thereof. > > What if ActionFlags was 'soft coded'. Rather like the physics model, > where there is a base definition, and then the definition can be > altered externally to add new controls. So everything uses the same > ActionFlags object everywhere, but the encoding can be altered by > changing that definition file. > > That also seems to provide a reasonable case for why you would need to > be generically creating your control configuration lists. > > BTW, I'm saying 'file' for lack of a better term. The new definition > would most likely come from some aspect of the Bios. I think something along these lines could work. > I don't find the concept of dynamically displaying available control > flags and tying controls to them a bad idea, just querying the > ActionFlags itself to be unusual. Even in this soft code case I would > lean towards either a separate class or the encoder (parse_keymap (and > mice!) object) handling the exposition of 'there's flag _trigger_1 with > name "Trigger 1" and it is a toggle-switch (No mouse or other analog > input need apply)' Whereas it seems perfectly natural to me to think that nobody knows better what an ActionFlags object is capable of representing than the ActionFlags object (or its class - but the general lack of support for runtime class stuff in C++ is quite a liability). How is an external object going to know? Does someone have to match the correct external object to the correct ActionFlags object? Why make them do that? Now as for actually presenting a UI, or handling key-configuration preferences in storage, etc. etc. yes external objects are entirely appropriate - and the same external object is useful with a variety of ActionFlags objects. > Also, OO doesn't necessarily mean classes. Marathon has things > sectioned off (ala portable_files.h/files_macintosh.c) itself. Right, just as classes don't mean OO. > Unfortunately for cocoa, I only seem to be able to focus on learning > code when I have something to do in it. AM is certainly being a great > learning experience. I have read Design Patterns and really wish I > could remember more of the one class I did take on Object Orientated > Analyses and Design. True. I was interested in learning about Cocoa for quite some time, but did not really get into it until I actually had a project to work on. (Once I did, though, I got spoiled fast. ;) ) OTOH I might have become a real Java convert if I had worked in it much (and if most Java resources out there weren't about putting some cutesy animation on your Web page). It seems to have many of the nice features of Cocoa, _plus_ a built-in strategy for limiting the capabilities of code (not just the 'sandbox' restricted-applet idea, but the fundamental design with the SecurityManager object). But, I never really got to know it as well. > I swear that seemed like the only Computer Course I took in school that > defined why it was useful to know and I kick myself that I can't > remember more of it. :) Believe it or not, I never had any classes on object-orientation... so I didn't "get it" until much later, principally from the two sources cited. :) Please try to take my comment about learning about OO in this context, rather than as some kind of slight or something. (But I think you did anyway.) Of course I still probably don't *really* "get it", I just think I do. Sort of like before. But I get it better now than I did before. :) > I honestly don't know how many threads we may have going on. All the more reason to have separate queues for separate tasks. :) > For decent Carbon control, I already feel a need to be able to invert > the relation of input thread and rendering/game update thread. This is because, essentially, parse_keymap() cannot be implemented in a way that will work in the non-main thread? If the problem's principally with the mouse control, it might be interesting to see how SDL (I mean SDL at least as much as A1/SDL) does it. IIRC the A1 Carbon version had this problem where the pointer could reach the end of the screen and would refuse to go further... but the Mac OS X SDL version had no such problem. At the least, in the Carbon version I'd end up starting Screen Effects a lot which was not a problem in Mac OS X SDL A1. > It's my impression with A1 that during network play, the usual input > thread is skipped, instead relying on a network input thread. For > Carbon would this then mean 3 threads going on? (rendering/update, > Carbon input, network tasks) This is exactly right. In fact, there's a fourth task, but it doesn't run all that often. In any circumstance, you at least have: Main thread - update/rendering Periodic task - capture input (or, in film playback, just move it from the recording_queues to the player_queues). In a netgame on the server, this may also send out network packets. In netgames you also have: Reactive task - runs code in response to packet arrival (often results in packet send) Periodic task - resend unacknowledged packet (in practice this is usually reset - in response to getting an ack - before it gets a chance to fire and run) Woody |
From: Br'fin <br...@ma...> - 2003-01-27 00:41:39
|
I'm working on revising the file abstraction stuff. My ISP was acting fruity, making some research awkward. I'm finding myself looking at the situation of CFileRef. And sure, I could make it masquerade as a FILE*. However it seems the right thing to do would be to instead go with iostream. This is C++ after all. Ends up with the same basic effect, we're not writing wrappers to a bunch of other files. Probably works out better too, everything outside of CFileDesc just treats the corresponding IF/OF stream version of CFileRef as a private declaration, not even available externally. :) This does beg the question whether things like other related elements, ie sprintf, dprintf or such should be handled in any similar way. :) -Jeremy Parsons |
From: Timothy C. <tco...@ha...> - 2003-01-26 22:35:03
|
>>> Given the importance of this, I want more detail than this. This >>> cannot be a "magic happens here." I want to know *how* you determine >>> it to be what kind of film file it is. >> >> Right, Tim is right on, sorry I thought it was pretty clear how it >> would work out. > > I know I'm being a pest about it. But no one (myself included) seems > to have explicitly jotted anything down in terms of mixed file support > Replays are just one case. I'm not exactly sure what you mean. It seems to me that the headers I mentioned should work with *any* file type we create. I admit I don't know much about the file types, or how to determine what type a file is without a header (beyond extension or type/creator codes), but a file header could be something like this: File type code (probably 4-char code, like we've always had) File version (a number, probably corresponding to the version of AM) **Not sure what we'd need after this, but maybe any or all of these would be useful: Size of file # of records Length of record Locations of records in file There might be more useful stuff that could go in a header. It's particularly useful if you set a fixed size for the header (padding with 0s where necessary); then you can just look at the first X bytes in the file and see if it looks familiar. Timothy Collett "There is a theory that states that if ever anyone discovers what the Universe is for and why it is here, it will instantly disappear and be replaced by something even more bizarre and inexplicable. There is another theory that states that this has already happened." - The Restaurant at the End of the Universe, by Douglas Adams |
From: Br'fin <br...@ma...> - 2003-01-26 22:10:18
|
On Sunday, January 26, 2003, at 01:08 PM, Woody Zenfell, III wrote: >> Given the importance of this, I want more detail than this. This >> cannot be a "magic happens here." I want to know *how* you determine >> it to be what kind of film file it is. > > Right, Tim is right on, sorry I thought it was pretty clear how it > would work out. I know I'm being a pest about it. But no one (myself included) seems to have explicitly jotted anything down in terms of mixed file support Replays are just one case. > An ActionFlags object encapsulates one player's inputs for one > game-tick. That is my definition. With 'inputs' here meaning > anything that affects the course of the game (i.e. helps to determine > a game-state). (Thus, things like changing the sound volume on the > local computer have no business being in ActionFlags - and of course > they're not, at the moment.) Agreed upon on the 'out of game' keys. >> To me they are a compact and concise way of passing encoded input >> data to the game engine. I can see how it would be useful to expand >> upon the allowable number of action flags. I haven't grasped why we >> need to be using multiple different kinds of ActionFlags in your >> queues. > > Compact and concise may on may not be important. Packing the flags > for film files or network packets etc. should produce a concise > representation to save disk space or packet space, respectively. But > I don't see any particular reason they need to be concise within the > game engine. Much of my reasoning for compact and concise is that the action flags were implemented initially as flags on a 32bit word. And I would think they were implemented that way for a reason. AM is probably going to be a touch thicker and slower than the original code design. But I would prefer more of the processing to go towards the rendering and gameworld updating of things. Please read on by the way. > Well you're right, if there's a strict lineage of > less-powerful-to-more-powerful (in terms of representational ability) > ActionFlags objects, then yes anything done in one representation can > be done in the most recent (and thus most powerful) representation. > > But if one person takes AM in one direction, and develops his own > ActionFlags representation with, I don't know, 'spitting', and > somebody else takes AM in another direction and has ActionFlags for > 'levitation', how do you handle that? It seems obvious to me that > when AM is playing the 'spitting' scenario, it uses the > 'spitting'-capable ActionFlags. When AM is playing the 'levitation' > scenario, it uses the 'levitation'-capable ActionFlags. It seems > quite useful to me to allow the type of ActionFlags to change. > (Though as noted in my other message, it doesn't seem useful to me to > have different ActionFlags types in the course of a single game. Thus > queues can depend on holding homogeneous ActionFlags.) > First, we don't have one person taking AM in one direction and someone else taking AM in a second direction. Secondly, they are more than welcome to fork AM and create their own project tailored for their game and needs. If one project has feature A and another has feature B. Neither necessarily has to be folded into the main codebase. Thirdly, my current impression is there's too much 'hard coding' in your thinking of ActionFlags. (Again, continue reading, I have no idea where to group my proposal around this) By 'hard coding' I mean explicitly putting everything into the ActionFlags or subclasses thereof. >>> Good question. Probably not too big though I would guess. I mean, >>> maybe you have to pay an additional 32 bits for a vtable pointer or >>> something? OTOH the benefits are many, I think - different >>> ActionFlags representations, switchable on-the-fly, without any >>> chance of getting them mixed up. >> >> 32 bits for the pointer to CActionFlags as well. In addition to >> whatever other overhead it being a class adds to the memory usage. >> This appears to be deviating from compact ActionFlags. > > 1. Who ever said anything about storing pointers? I was picturing > the queues etc. storing the ActionFlags objects directly in their > chunks of memory, a la STL vector et al. This is possible since every > ActionFlags object in use at a given moment is of the same type. AM, is, alas, C++. To my knowledge C++ starts breaking down when you start trying to pass a subclass by value to a class expecting the master class. I think this is why Java implicitly treats every object as essentially a pointer to that object. I didn't know you could ask for a vector<I don't know what class to use until runtime>. Ok, here's the point I was asking you to read on to: What if ActionFlags was 'soft coded'. Rather like the physics model, where there is a base definition, and then the definition can be altered externally to add new controls. So everything uses the same ActionFlags object everywhere, but the encoding can be altered by changing that definition file. That also seems to provide a reasonable case for why you would need to be generically creating your control configuration lists. BTW, I'm saying 'file' for lack of a better term. The new definition would most likely come from some aspect of the Bios. I don't find the concept of dynamically displaying available control flags and tying controls to them a bad idea, just querying the ActionFlags itself to be unusual. Even in this soft code case I would lean towards either a separate class or the encoder (parse_keymap (and mice!) object) handling the exposition of 'there's flag _trigger_1 with name "Trigger 1" and it is a toggle-switch (No mouse or other analog input need apply)' > If you don't see the benefits of object-orientation, perhaps you have > not read _Design Patterns_ or worked in Cocoa. I recommend both; I > didn't understand OO prior to them (though I thought I did ;) ). I agree with the benefits of object-orientation. But they aren't the be all and end all of everything too. I don't have a problem applying OO to most things in AM, just specific things. ActionFlags with its initial bias to flags within a 32bit word feels like one of those. Though I do agree isn't as horrid as trying to fit OO into the heart of the renderer. Also, OO doesn't necessarily mean classes. Marathon has things sectioned off (ala portable_files.h/files_macintosh.c) itself. Unfortunately for cocoa, I only seem to be able to focus on learning code when I have something to do in it. AM is certainly being a great learning experience. I have read Design Patterns and really wish I could remember more of the one class I did take on Object Orientated Analyses and Design. I swear that seemed like the only Computer Course I took in school that defined why it was useful to know and I kick myself that I can't remember more of it. :) > Whatever starts up the game 'wires up' flag producers and consumers > (which communicate with one another via ActionQueues). Flag producers > and consumers then just do their job with no knowledge of what's on > the other side of the Queues it's using. Ahhh, ok. >> Ok, just read up on that. Ditching and smearing both look like >> dangerous behaviors. In general I presume whenever this happens that >> the local machine's gameworld is only updated with the final set of >> flags that the network stuff 'found'. > > I don't know what you mean by 'dangerous'. Both are entirely > necessary to account for clock drift between machines (and may be > useful in coping with changing latency conditions). Dangerous. As in, with my imperfect understanding of the way things work currently, it sounds like it could aid in creating out of sync errors. I accept your (and Bungie's) reasoning here. >> Why the distinction between RealActionQueue and the recording queue >> when playing (single or network)? Can't a recording task just peek at >> the RealActionQueue for what it needs? > > No, the idea is that any given queue has only one writer and one > reader, to keep synchronization simple. While it may be the case > currently that the readers of the RealActionQueue are both in the same > thread, and thus have a natural mutual-exclusion property, I don't > think we want to unnecessarily bind ourselves to that arrangement. Ok about the synchronization and the reading issues. > And, as pointed out, I don't think we want to protect every queue > access with a mutex. (We have different ideas about what kinds of > overhead are acceptable apparently. ;) ) I honestly don't know how many threads we may have going on. For decent Carbon control, I already feel a need to be able to invert the relation of input thread and rendering/game update thread. It's my impression with A1 that during network play, the usual input thread is skipped, instead relying on a network input thread. For Carbon would this then mean 3 threads going on? (rendering/update, Carbon input, network tasks) I admit I could just be being thick. > Also with each reader (the recording film-dumper and the game-state > updater) having its own copy of the flag data, they can operate more > independently, each handling the data at whatever rate it feels like > without worrying about what the other guy's doing. For example, the > current film packing code waits for something like 256 flags to > accumulate in its queue for each player, then writes them out in > interleaved, RLE'd chunks. How would the code do that if it were > trying to look over the game-updater's shoulder? Make a copy of all > the flags? :) (Yeah yeah this may not be the best way for us to > produce our films, but it's an example of a specific use...) Heh, we really should deal with that, or have a way for the replay to say 'last block is shorter than normal' so it can finish off the replay properly at point of exit from the game. :) -Jeremy Parsons |
From: Woody Z. I. <woo...@sb...> - 2003-01-26 18:09:00
|
On Sunday, January 26, 2003, at 04:28 AM, Br'fin wrote: > On Friday, January 24, 2003, at 04:57 PM, Woody Zenfell, III wrote: > >> On Friday, January 24, 2003, at 01:29 PM, Br'fin wrote: >> >>> Well, my initial fears of messing with the action flags involve >>> breaking the M2 Films. If you're going to talk about changing how the >>> action flags are stored, then you better detail how to identify and >>> handle replays from different versions of the code. I don't care so >>> much if our films don't run under Marathon, but we should be able to >>> handle theirs, always. >> >> Yeah simple, the code that handles a replay examines the file, >> determines that it's a "traditional" film file, and sets up to use the >> MarathonFilmUnpacker. Same deal on output (just use a >> MarathonFilmPacker, which writes M2-compatible films, when starting up >> a MarathonGame). > > Given the importance of this, I want more detail than this. This cannot > be a "magic happens here." I want to know *how* you determine it to be > what kind of film file it is. Right, Tim is right on, sorry I thought it was pretty clear how it would work out. Whatever object is trying to mediate the opening of the file hands the data chunk off to some sort of demultiplexer. The demultiplexer's job is to figure out which FilmUnpacker (or WadUnpacker, etc.) should be used for the file - probably by asking several such objects in turn to see which one is willing to handle it. (If multiple are willing, they could give a numeric score as to how well they think they could handle it, etc.) >> Why? If the ActionFlags class is what knows which sorts of things it >> can represent and what types of values it can store for those things, >> why should it not be able to share that information with some kind of >> UI configuration object (which builds the configuration dialog/etc. >> based on what the ActionFlags class says)? > > One thing that occurred to me is just to start off your document with a > discussion of the perceived role of ActionFlags to begin with. Just to > make sure we're all on the same page with what they are and what they > should be. And what they definitely are not. An ActionFlags object encapsulates one player's inputs for one game-tick. That is my definition. With 'inputs' here meaning anything that affects the course of the game (i.e. helps to determine a game-state). (Thus, things like changing the sound volume on the local computer have no business being in ActionFlags - and of course they're not, at the moment.) > To me they are a compact and concise way of passing encoded input data > to the game engine. I can see how it would be useful to expand upon the > allowable number of action flags. I haven't grasped why we need to be > using multiple different kinds of ActionFlags in your queues. Compact and concise may on may not be important. Packing the flags for film files or network packets etc. should produce a concise representation to save disk space or packet space, respectively. But I don't see any particular reason they need to be concise within the game engine. Well you're right, if there's a strict lineage of less-powerful-to-more-powerful (in terms of representational ability) ActionFlags objects, then yes anything done in one representation can be done in the most recent (and thus most powerful) representation. But if one person takes AM in one direction, and develops his own ActionFlags representation with, I don't know, 'spitting', and somebody else takes AM in another direction and has ActionFlags for 'levitation', how do you handle that? It seems obvious to me that when AM is playing the 'spitting' scenario, it uses the 'spitting'-capable ActionFlags. When AM is playing the 'levitation' scenario, it uses the 'levitation'-capable ActionFlags. It seems quite useful to me to allow the type of ActionFlags to change. (Though as noted in my other message, it doesn't seem useful to me to have different ActionFlags types in the course of a single game. Thus queues can depend on holding homogeneous ActionFlags.) > As to UI configuration support, that could be a role for the encoder, > but seems out of scope for the ActionFlags themselves. Whatever. If you don't want your ActionFlags to be able to tell other objects what they can represent (which is all I'm suggesting), I mean, it's your project. But it seems like a poor decision. > Another thing on ActionFlags. They strike me as something that should > always be constant for a particular version of the game. There could be > flags to say 'if in M2 mode, ignore action feature X', but that's in > the game core and not in the action flag itself. Compatibility of > things like replays is the responsibility of the replay loader and it > works by munging the prior ActionFlags into the current internal > version. Whereas in my view, you would simply use the M2ActionFlags type internally. No munging necessary. >> Good question. Probably not too big though I would guess. I mean, >> maybe you have to pay an additional 32 bits for a vtable pointer or >> something? OTOH the benefits are many, I think - different >> ActionFlags representations, switchable on-the-fly, without any chance >> of getting them mixed up. > > 32 bits for the pointer to CActionFlags as well. In addition to > whatever other overhead it being a class adds to the memory usage. This > appears to be deviating from compact ActionFlags. 1. Who ever said anything about storing pointers? I was picturing the queues etc. storing the ActionFlags objects directly in their chunks of memory, a la STL vector et al. This is possible since every ActionFlags object in use at a given moment is of the same type. 2. I really feel that a couple extra kilobytes of runtime memory is not too high a price to pay for the benefits. In my view, object-orientation is the best model we have now for expressing modularity, and so modularizing code almost inevitably means making it object-oriented. There is some additional overhead in both time and space, but in the long run it's almost certainly worth it for the added flexibility and maintenance benefits. If you don't see the benefits of object-orientation, perhaps you have not read _Design Patterns_ or worked in Cocoa. I recommend both; I didn't understand OO prior to them (though I thought I did ;) ). >>> As for the others it seems like when you start a game you setup the >>> current 'flag pipe' that every 30 times a second pulls from a source >>> queue and throws to the player queues. A replay would be 'read from >>> file, pipe to player queues' and a network flag pipe would be 'read >>> from input queue, handle networking, put all player flags on player >>> queue' >> >> Yeah this is a restatement of what I suggested, right? > > I'm not sure. Or else I wouldn't have said it like I did. I wasn't > quite understanding what you had written up before. Whatever starts up the game 'wires up' flag producers and consumers (which communicate with one another via ActionQueues). Flag producers and consumers then just do their job with no knowledge of what's on the other side of the Queues it's using. >>> I'm curious as to what you mean by smearing. > [snip] >> I think my overview of networking and input covers the role of >> smearing. >> > Ok, just read up on that. Ditching and smearing both look like > dangerous behaviors. In general I presume whenever this happens that > the local machine's gameworld is only updated with the final set of > flags that the network stuff 'found'. I don't know what you mean by 'dangerous'. Both are entirely necessary to account for clock drift between machines (and may be useful in coping with changing latency conditions). Currently yes, as you can see from my overview of the current scheme, the game-world update stuff operates only on action_flags that have been around the ring on the network. Ditching and smearing happen between the local_queue and the flags' being put into the network packet. Thus, the game-world update stuff only sees post-ditch, post-smear flags. Note that for the purposes of prediction, it may be useful to move ditching and smearing to an earlier point in the process, informed by information at the network level, so we can avoid mispredicting the local player (which we've both stated is a Bad Thing). But I see no reason to worry about that at the moment (beyond to point out that it's a possibility). >> If the game core can't update faster than 30fps, we're hosed (if we're >> planning on playing in realtime that is ;) ). There's no way around >> it. > > I don't know if it can. I haven't looked into it. But I would presume > the 30fps game core is > fairly fixed in the logic and calculations of all the elements in the > game. I think there's a way to pretty things up so they appear to be > running faster than 30fps, but it doesn't allow things to be processed > faster than that. I think I must have misunderstood what you meant. Heh heh. I read it as "if the game core can't run quickly enough to churn out 30 updated game-states per second". Sounds like you were talking about increasing the tick_rate. The latter sounds very difficult to me, at least if preserving backwards-compatibility is important (and we seem to agree that it is). So some kind of interpolation beyond the standard updating I think is what's in order. But we've both talked about that a fair amount I think. >> Ok, to make things clearer (maybe), I've created a PDF diagramming the >> flows of action_flags in a networked game, a single-player game, and >> in film playback. (It illustrates how Marathon/A1 do it, which is the >> same way I am picturing things working for AM, albeit more loosely >> tied together and perhaps with different routines actually doing the >> work.) I'm sending it to Jesse and asking him to post it at s.b.o. >> > Why the distinction between RealActionQueue and the recording queue > when playing (single or network)? Can't a recording task just peek at > the RealActionQueue for what it needs? No, the idea is that any given queue has only one writer and one reader, to keep synchronization simple. While it may be the case currently that the readers of the RealActionQueue are both in the same thread, and thus have a natural mutual-exclusion property, I don't think we want to unnecessarily bind ourselves to that arrangement. And, as pointed out, I don't think we want to protect every queue access with a mutex. (We have different ideas about what kinds of overhead are acceptable apparently. ;) ) Also with each reader (the recording film-dumper and the game-state updater) having its own copy of the flag data, they can operate more independently, each handling the data at whatever rate it feels like without worrying about what the other guy's doing. For example, the current film packing code waits for something like 256 flags to accumulate in its queue for each player, then writes them out in interleaved, RLE'd chunks. How would the code do that if it were trying to look over the game-updater's shoulder? Make a copy of all the flags? :) (Yeah yeah this may not be the best way for us to produce our films, but it's an example of a specific use...) Woody |
From: Timothy C. <tco...@ha...> - 2003-01-26 14:14:08
|
>>> Well, my initial fears of messing with the action flags involve >>> breaking the M2 Films. If you're going to talk about changing how >>> the action flags are stored, then you better detail how to identify >>> and handle replays from different versions of the code. I don't care >>> so much if our films don't run under Marathon, but we should be able >>> to handle theirs, always. >> >> Yeah simple, the code that handles a replay examines the file, >> determines that it's a "traditional" film file, and sets up to use >> the MarathonFilmUnpacker. Same deal on output (just use a >> MarathonFilmPacker, which writes M2-compatible films, when starting >> up a MarathonGame). > > Given the importance of this, I want more detail than this. This > cannot be a "magic happens here." I want to know *how* you determine > it to be what kind of film file it is. > One relatively simple way (I think) would be to give our new, M2/Inf-incompatible film format a header telling what version of film it is. Thus, the first thing AM can do when opening a film is to look for this header. If it's there, treat it as the version it says it is. If it's not, attempt to open it as an M2 film. (Obviously, it'll get into trouble if, say, someone tries to open their word processing document as a film, but I don't think we need to worry too much about that kind of really dumb behaviour :-) Timothy Collett "But what ... is it good for?" --Engineer at the Advanced Computing Systems Division of IBM, 1968, commenting on the microchip |
From: Br'fin <br...@ma...> - 2003-01-26 10:28:42
|
On Friday, January 24, 2003, at 04:57 PM, Woody Zenfell, III wrote: > On Friday, January 24, 2003, at 01:29 PM, Br'fin wrote: > >> Well, my initial fears of messing with the action flags involve >> breaking the M2 Films. If you're going to talk about changing how the >> action flags are stored, then you better detail how to identify and >> handle replays from different versions of the code. I don't care so >> much if our films don't run under Marathon, but we should be able to >> handle theirs, always. > > Yeah simple, the code that handles a replay examines the file, > determines that it's a "traditional" film file, and sets up to use the > MarathonFilmUnpacker. Same deal on output (just use a > MarathonFilmPacker, which writes M2-compatible films, when starting up > a MarathonGame). Given the importance of this, I want more detail than this. This cannot be a "magic happens here." I want to know *how* you determine it to be what kind of film file it is. >>> It would be nice if the ActionFlags class could help the >>> configure-controllers UI figure out what controls should be >>> configured and what type of input they expect (boolean or numeric, >>> etc.). >> >> Perhaps beyond the scope of the ActionFlags class. but I agree on the >> concept of something converting raw input into an ActionFlags. > > Why? If the ActionFlags class is what knows which sorts of things it > can represent and what types of values it can store for those things, > why should it not be able to share that information with some kind of > UI configuration object (which builds the configuration dialog/etc. > based on what the ActionFlags class says)? One thing that occurred to me is just to start off your document with a discussion of the perceived role of ActionFlags to begin with. Just to make sure we're all on the same page with what they are and what they should be. And what they definitely are not. To me they are a compact and concise way of passing encoded input data to the game engine. I can see how it would be useful to expand upon the allowable number of action flags. I haven't grasped why we need to be using multiple different kinds of ActionFlags in your queues. As to UI configuration support, that could be a role for the encoder, but seems out of scope for the ActionFlags themselves. Another thing on ActionFlags. They strike me as something that should always be constant for a particular version of the game. There could be flags to say 'if in M2 mode, ignore action feature X', but that's in the game core and not in the action flag itself. Compatibility of things like replays is the responsibility of the replay loader and it works by munging the prior ActionFlags into the current internal version. >>> If the frequent use of virtual functions instead of inline code >>> makes anyone queasy, refer to my previous post about optionally >>> selecting a specific subclass at build-time rather than at runtime. >> What is the impact/overhead going to be of going to a class instead >> of just going to a 64bit value for more flags? I do accept the value >> of optimization in this case, BTW :) > > Good question. Probably not too big though I would guess. I mean, > maybe you have to pay an additional 32 bits for a vtable pointer or > something? OTOH the benefits are many, I think - different > ActionFlags representations, switchable on-the-fly, without any chance > of getting them mixed up. 32 bits for the pointer to CActionFlags as well. In addition to whatever other overhead it being a class adds to the memory usage. This appears to be deviating from compact ActionFlags. >> As for the others it seems like when you start a game you setup the >> current 'flag pipe' that every 30 times a second pulls from a source >> queue and throws to the player queues. A replay would be 'read from >> file, pipe to player queues' and a network flag pipe would be 'read >> from input queue, handle networking, put all player flags on player >> queue' > > Yeah this is a restatement of what I suggested, right? I'm not sure. Or else I wouldn't have said it like I did. I wasn't quite understanding what you had written up before. >> I'm curious as to what you mean by smearing. [snip] > I think my overview of networking and input covers the role of > smearing. > Ok, just read up on that. Ditching and smearing both look like dangerous behaviors. In general I presume whenever this happens that the local machine's gameworld is only updated with the final set of flags that the network stuff 'found'. >> Or should we just be starting up a thread to cover networking, the >> game core, animation, and 'what the heck do we do if the game core >> can't update faster than 30 fps. and can't even manage that under >> networked situations'? > > In this context does "thread" mean "discussion on the list"? Yes. Darn multiple meanings of words and network threads on the brain. > If the game core can't update faster than 30fps, we're hosed (if we're > planning on playing in realtime that is ;) ). There's no way around > it. I don't know if it can. I haven't looked into it. But I would presume the 30fps game core is fairly fixed in the logic and calculations of all the elements in the game. I think there's a way to pretty things up so they appear to be running faster than 30fps, but it doesn't allow things to be processed faster than that. > Ok, to make things clearer (maybe), I've created a PDF diagramming the > flows of action_flags in a networked game, a single-player game, and > in film playback. (It illustrates how Marathon/A1 do it, which is the > same way I am picturing things working for AM, albeit more loosely > tied together and perhaps with different routines actually doing the > work.) I'm sending it to Jesse and asking him to post it at s.b.o. > Why the distinction between RealActionQueue and the recording queue when playing (single or network)? Can't a recording task just peek at the RealActionQueue for what it needs? -Jeremy Parsons |
From: Br'fin <br...@ma...> - 2003-01-25 12:17:05
|
I agree that when we design design our own methodology for expanding the game that we want to shy away from resources. The main reason for including them at all is for Marathon style compatibility. -Jeremy Parsons On Friday, January 24, 2003, at 07:24 PM, Timothy Collett wrote: > OK, first off, this is really neat, being able to actually understand > how all this stuff works. Seems to me that armed with this sort of > knowledge, it shouldn't be hard to track down the dreaded out-of-sync > bugs once and for all...anyway, I only understand a little more than > half of this, but one thing I think I got was a "what are we doing > about resource forks/files in the future?" Am I right? > > Well, even if I'm not, I'd like to cast a vote against them. It would > appear that using resource forks/files, while it makes it a bit easier > on Macs, makes it more confusing on other platforms, and they're not > even properly part of OS X anymore. So it seems like it would make > sense to kill them, and use formats that everyone can use equally well > (or badly :-) > > What do the important people (read: the coders) think? |
From: Timothy C. <tco...@ha...> - 2003-01-25 00:24:50
|
OK, first off, this is really neat, being able to actually understand how all this stuff works. Seems to me that armed with this sort of knowledge, it shouldn't be hard to track down the dreaded out-of-sync bugs once and for all...anyway, I only understand a little more than half of this, but one thing I think I got was a "what are we doing about resource forks/files in the future?" Am I right? Well, even if I'm not, I'd like to cast a vote against them. It would appear that using resource forks/files, while it makes it a bit easier on Macs, makes it more confusing on other platforms, and they're not even properly part of OS X anymore. So it seems like it would make sense to kill them, and use formats that everyone can use equally well (or badly :-) What do the important people (read: the coders) think? Timothy Collett He who asks is a fool for five minues. He who doesn't ask is a fool forever. |
From: Woody Z. I. <woo...@sb...> - 2003-01-24 21:57:50
|
On Friday, January 24, 2003, at 01:29 PM, Br'fin wrote: > Well, my initial fears of messing with the action flags involve > breaking the M2 Films. If you're going to talk about changing how the > action flags are stored, then you better detail how to identify and > handle replays from different versions of the code. I don't care so > much if our films don't run under Marathon, but we should be able to > handle theirs, always. Yeah simple, the code that handles a replay examines the file, determines that it's a "traditional" film file, and sets up to use the MarathonFilmUnpacker. Same deal on output (just use a MarathonFilmPacker, which writes M2-compatible films, when starting up a MarathonGame). > Input-wise, parse_keymap and other input gathering needs its own > abstraction. After some of the problems I've had with GetKeys under > MacOS X (It has a high tendency to just plain not work after you have > to enter a Keychain or admin password into an application) I want some > other mechanism in there. This can be separate from translating the > inputs into the actual ActionFlags. Like I said, other (per-platform, presumably) objects can help take care of the translation of input to ActionFlags. > Also, how much room for growth does the current ActionFlags allow? A > quick glance seems to reveal 0 currently. (Though I must say I don't > know what all the bits do) I believe the ActionFlags are pretty well used up in "normal" mode. In "terminal" mode there is probably still plenty of room. >> It would be nice if the ActionFlags class could help the >> configure-controllers UI figure out what controls should be configured >> and what type of input they expect (boolean or numeric, etc.). > > Perhaps beyond the scope of the ActionFlags class. but I agree on the > concept of something converting raw input into an ActionFlags. Why? If the ActionFlags class is what knows which sorts of things it can represent and what types of values it can store for those things, why should it not be able to share that information with some kind of UI configuration object (which builds the configuration dialog/etc. based on what the ActionFlags class says)? >> If the frequent use of virtual functions instead of inline code makes >> anyone queasy, refer to my previous post about optionally selecting a >> specific subclass at build-time rather than at runtime. > What is the impact/overhead going to be of going to a class instead of > just going to a 64bit value for more flags? I do accept the value of > optimization in this case, BTW :) Good question. Probably not too big though I would guess. I mean, maybe you have to pay an additional 32 bits for a vtable pointer or something? OTOH the benefits are many, I think - different ActionFlags representations, switchable on-the-fly, without any chance of getting them mixed up. >> Queues would likely be implemented as they are now - as circular >> queues - to control memory allocation and to simplify communication >> between a pair of threads. (Note that one thread must be consistently >> the 'enqueuer' and one (potentially different) thread consistently the >> 'peeker'/'dequeuer' - or else that multiple threads trying to work the >> same half mediate their access on a mutex or something - for this to >> work safely.) > > Aim for the mutex locks, methinks. Well, where needed, of course. One of the principal features of a circular queue is that a single enqueueing thread and a single dequeueing thread can safely communicate through it without any locking. >> So these elements should all be configurable with "what queue should I >> read from" and "what queue should I write to"; when starting a netgame >> or a single-player game or a film replay etc. they're all wired up the >> way they ought to be. > > I don't know about a recording queue per-se. I forget when it's hooked > into the scheme of things right now (Especially for network games) But > I think Something along the lines of 'record player quests' for the > current set of actions before updating the current world based upon > those sync'd actions is acceptable. I'm not sure what you're saying here, or how it differs from what I suggested or how it differs from the current state of things. (FWIW what I suggested is very close to the current state of things, overall, just cleaned up and modularized where possible and seemingly useful.) > As for the others it seems like when you start a game you setup the > current 'flag pipe' that every 30 times a second pulls from a source > queue and throws to the player queues. A replay would be 'read from > file, pipe to player queues' and a network flag pipe would be 'read > from input queue, handle networking, put all player flags on player > queue' Yeah this is a restatement of what I suggested, right? > I'm curious as to what you mean by smearing. When the network code needs action_flags for the local player to share with the other machines (on ring packet arrival), but the local NetQueueingTask() (which is the Input Capturer) hasn't yet queued up an action_flags on the LocalQueue, the network code goes directly to the Input Capturer to get action_flags to fill in. My suggestion lets the network code effectively make up those action_flags on its own, based on its recollection of the most recent action_flags it had for the player (and on the ActionFlags's GuessNextFlags() routine), so the Input Capturer's code doesn't have to worry about threading problems etc. I think my overview of networking and input covers the role of smearing. > Or should we just be starting up a thread to cover networking, the game > core, animation, and 'what the heck do we do if the game core can't > update faster than 30 fps. and can't even manage that under networked > situations'? In this context does "thread" mean "discussion on the list"? I think the current timing characteristics of M2/A1 are right on, but I'd be willing to listen to other suggestions. (Moving to client/server may alter the desired timing, for example.) I'd ask anyone planning to participate in the discussion to read my overview of the current input system at http://source.bungie.org/_enginedevelopment/reference/networking- input.html If the game core can't update faster than 30fps, we're hosed (if we're planning on playing in realtime that is ;) ). There's no way around it. OTOH not being able to render at 30fps is (obviously) ok. And transient bubbles in the scheduling of the other elements are absorbed by the queues. Ok, to make things clearer (maybe), I've created a PDF diagramming the flows of action_flags in a networked game, a single-player game, and in film playback. (It illustrates how Marathon/A1 do it, which is the same way I am picturing things working for AM, albeit more loosely tied together and perhaps with different routines actually doing the work.) I'm sending it to Jesse and asking him to post it at s.b.o. Woody |
From: Br'fin <br...@ma...> - 2003-01-24 19:29:28
|
Well, my initial fears of messing with the action flags involve breaking the M2 Films. If you're going to talk about changing how the action flags are stored, then you better detail how to identify and handle replays from different versions of the code. I don't care so much if our films don't run under Marathon, but we should be able to handle theirs, always. Input-wise, parse_keymap and other input gathering needs its own abstraction. After some of the problems I've had with GetKeys under MacOS X (It has a high tendency to just plain not work after you have to enter a Keychain or admin password into an application) I want some other mechanism in there. This can be separate from translating the inputs into the actual ActionFlags. Also, how much room for growth does the current ActionFlags allow? A quick glance seems to reveal 0 currently. (Though I must say I don't know what all the bits do) > It would be nice if the ActionFlags class could help the > configure-controllers UI figure out what controls should be configured > and what type of input they expect (boolean or numeric, etc.). Perhaps beyond the scope of the ActionFlags class. but I agree on the concept of something converting raw input into an ActionFlags. > If the frequent use of virtual functions instead of inline code makes > anyone queasy, refer to my previous post about optionally selecting a > specific subclass at build-time rather than at runtime. What is the impact/overhead going to be of going to a class instead of just going to a 64bit value for more flags? I do accept the value of optimization in this case, BTW :) > Queues would likely be implemented as they are now - as circular > queues - to control memory allocation and to simplify communication > between a pair of threads. (Note that one thread must be consistently > the 'enqueuer' and one (potentially different) thread consistently the > 'peeker'/'dequeuer' - or else that multiple threads trying to work the > same half mediate their access on a mutex or something - for this to > work safely.) Aim for the mutex locks, methinks. > So these elements should all be configurable with "what queue should I > read from" and "what queue should I write to"; when starting a netgame > or a single-player game or a film replay etc. they're all wired up the > way they ought to be. I don't know about a recording queue per-se. I forget when it's hooked into the scheme of things right now (Especially for network games) But I think Something along the lines of 'record player quests' for the current set of actions before updating the current world based upon those sync'd actions is acceptable. As for the others it seems like when you start a game you setup the current 'flag pipe' that every 30 times a second pulls from a source queue and throws to the player queues. A replay would be 'read from file, pipe to player queues' and a network flag pipe would be 'read from input queue, handle networking, put all player flags on player queue' > Random note that didn't fit anywhere else: currently the Input > Capturer code (parse_keymap() at the moment, basically, I think) must > be safe to run in both the input-capture thread (for normal use) and > in the network thread (in case the network code needs a "smear"). > Using mostRecentActionFlags->GetPredictedNextFlags() for smearing, > instead of tripping back to to the Input Capturer directly, makes the > scheduling behavior a little simpler and thus probably a little safer. > I'm curious as to what you mean by smearing. Or should we just be starting up a thread to cover networking, the game core, animation, and 'what the heck do we do if the game core can't update faster than 30 fps. and can't even manage that under networked situations'? -Jeremy Parsons |
From: Alexander S. <ast...@it...> - 2003-01-24 19:27:04
|
On Friday, January 24, 2003, at 01:32 PM, Br'fin wrote: >> What about "safe saves"? Should there be sort of built-in support >> for that sort of thing at a fairly low level? > > Now that seems pretty darn reasonable to try and work in. And would be > appropriate for most things we're trying to save. (Not sure about > replays, but I agree whole heartedly with respect to save games and > preferences) Atomic file operations should definitely be there. And they should be the default. (this is what the "safe save" stuff is) |
From: Br'fin <br...@ma...> - 2003-01-24 18:32:43
|
On Friday, January 24, 2003, at 10:55 AM, Woody Zenfell, III wrote: > On Friday, January 24, 2003, at 01:39 AM, Br'fin wrote: > >> First there was portable_files.h and it was good in concept. But >> there was only a Macintosh implementation. And even so, there was an >> evident bios in the structure towards the Macintosh. > > And perhaps rightly so, at least for things like resource forks that > we have to deal with but which (obviously) impose a Mac bias. (I > haven't reviewed portable_files.h, so maybe there is more Mac bias > than the "some" that needs to be there.) The funny thing is, portable_files.h wasn't dealing with the resource forks. I admit it's probably the case that it was being cast to an FSSpec and then opened as a resource fork though. I don't have a problem with adding a open_resource_file to CFileDesc. > So will there be a standard interface for things like "locate files > that match my filter" (which may be used for populating a file > browsing dialog) etc.? Such a thing would also be useful for a set of > paths to search for a given file. I have not yet looked over find_files.h yet. Though it seems to me that find_files would be the place to expand upon for this kind of functionality. > So far this is a comment, an observation, not a quibble or > recommendation etc., but... note that if you have multiple factories > at once, the above construction requires that the program know which > factory it should use at all times. My own bad. I should have specified that CFileDescFactory is a singleton. Thus the application should always know which factory to use. > If directory FileDescs that came from Factory A should always produce > FileDescs from Factory A (and never from Factory B), then it would be > better to have FileDescs know which factory they came from (either by > virtue of the class structure, or by explicitly keeping a reference to > the factory) and ask a FileDesc for a sub-FileDesc. (Or ask a > FileDesc for its factory, and ask that for a sub-FileDesc, which maybe > should be an option but asking the FileDesc directly is provided as a > convenience method.) There are some issues here I have been waffling on. CFileDesc could either be modfiable or constant. I've been leaning towards it being constant and that if you need a new path (a parent or a child) then you need to create a new CFileDesc. Now I've been mixed as to whether this should be a function of the factory or of the file desc itself. GetParent definitely strikes me as a member of CFileDesc, but I've been hazier on children. I don't have any great problem with CFileDesc *newDesc = preferencesDesc->Concatenate("FileName") >> The factory is also the most reasonable place to provide access to >> CFileDescs for specific platform specific places. Ala preferences >> directory and application directory >> >> CFileDesc *foo = FILE_DESC_FACTORY- >> >GetSpecificDir(CFileDescFactory::ApplicationsDirectory); >> // It is an error to delete this foo >> >> Specific directories so far are >> ApplicationDirectory - The directory the application has been run >> from >> (Current dir if command line, directory of application executable >> otherwise) >> PreferencesDirectory > > Note that some platforms may want to install and find data files > somewhere other than the application directory. In general I think > having more "specific directories", even if they map to the same real > directory in practice, is going to be a Good Thing as it gives > different platforms the opportunity to put things where they want at a > finer granularity. What other directories should I be including in the list? Looking at FileHandler.h in A1 also turns up the following: Save game directory Recording directory Resources directory - Directory for additional platform generic files required by A1 ie, at one time this was the Pfhortran Language File in A1 Though I suppose it could also cover SDL's required graphic files for its ui > Maybe on the whole I'm not understanding the role this plays in a > larger system, but I have questions like: > what about searching multiple places? what _about_ support for file > browsing? what about retrieving a directory's contents? Some of these seem like uses for find_files.h. Support for file browsing I didn't think of probably because I'm used to the various Mac file browsers. (StdFileDialog, NavServices) And so the need to support file browsing is kinda foreign. I can understand the troubles we're having with preferences implementation and SDL gui vs Mac GUI on that, but I'm not sure what to make of 'but our platform doesn't have a file browsing call' That being said, it does seem like there's some cause to split up FileDesc's current functionality so that we have a DirectoryDesc and a FileDesc. The other option would be to add details like IsFile, IsDir, open_directory or what not on CFileDesc. Having separate classes does seem appropriate for clarity. Though when you do get a deeper entity from the directory, how do you know if it's a file or a directory? Hsm, one class for both has its merits... > etc. I like centralizing (in a Factory) all the explicit paths etc. > - and since every FileDesc will come from one of these root FileDescs > (right? well except for FileDescs that come from the OS-native > representation I guess, but the Factory could be used for those too), I think it gets harder to have the factory create the FileDescs from the os-native representation. You can't put calls like CreateDesc(MacSpecificType) on the base factory declaration :) I don't see a problem with CFileDesc *MacSaveDialog(), which needs to know the platform stuff anyways going new MacCFileDesc(MacSpecificType) > the Factory gets to determine the C++ type of the FileDesc we end up > using - but I am not sure what other benefits it offers (e.g., why > _is_ it better to ask the Factory for a FileDesc every time we want a > file within a directory, instead of asking the directory's FileDesc?). That is a very good question. And you're right, having to go through the factory for that does indeed seem awkward. >> CFileRef represents an open file >> >> Operations include >> get_fpos >> set_fpos >> set_eof >> get_file_length >> read_file >> write_file >> close_file > > And a CFileRef is, I hope, a subclass of some more-general "read and > write chunks of data" scheme that's also the basis for the "Packing" > (serialization) stuff, rather like SDL_RWops, that provides the same > interface for working with chunks of data in memory as it does for > working with chunks of data in files. (SDL_RWops provides seek, tell, > get length, close, and various read and write operations, both "raw" > and packing- (and byte-swapping-) oriented.) > > A CFileRef would be allowed to return its CFileSpec on request of > course. Memory chunks would return a special value (maybe NULL?) > instead to indicate they're not file-backed. I'm not sure whether > this involves a dynamic_cast<> and a subclass-specific query, or a > "top-level" query to the CDataRef itself. (There may wish to be a > corresponding method to get the base address of a CBufferRef also, for > example.) Not really, portable_files itself was working at a low level. So it's not very intelligent. It's really not doing anything more than wrapping the stdio calls or platform specific elements. 'intelligence' comes from elements above that use it. Most of which tend to treat the file as a wad file, a resource file, or an ordered file with a header (ala shapes and sounds) Now I agree that there should be a further abstraction over and above the wad file to specifically detail what needs to be saved and perhaps how. > What about "safe saves"? Should there be sort of built-in support for > that sort of thing at a fairly low level? Now that seems pretty darn reasonable to try and work in. And would be appropriate for most things we're trying to save. (Not sure about replays, but I agree whole heartedly with respect to save games and preferences) > Man all this reinventing-the-wheel stuff really does make me wish we > were sitting on top of libFoundation or something ;) I guess the > important question to ask is "what flexibility/better > platform-dependent conformance/etc. are we buying with this extra > layer of wrapping?" I mean, why NOT just use some kind of standard > mechanism like iostreams or stdio? at least for most of the > interface? (you could supplement those mechanisms with some kind of > abstraction only where needed.) I mean, they're there, they're > familiar, they're tested, and they're not going to be going away any > time soon. (I recognize that there are benefits to wrapping. I would > just like to see them made explicit as part of the > reasoning/justification.) A Very reasonable question. And I doubt it would be that hard to recreate CFileRef as 'a c-style file handle with extra goodies' that could self-cast itself so that you could go fprintf(CFileRef, "my string"); The initial reasons probably have to do with Marathon handling paths as FSSpecs. portable_files then handles the minimums Bungie required for accessing files. If we can encapsulate the FSSpec with CFileDesc so that the result of an open is always the result of stdio fopen, then we could very well buy ourselves a good deal. > Anyway please don't take any of the above really as 'argument', I'm > glad to see a specific suggestion as a starting point and hope that a > discussion (including "devil's advocate" questions to expose more > detail and strengthen the underlying reasoning) will bring to light > any appropriate alterations. Like I said, I don't have a good picture > of where CFileSpec fits into the bigger structure, so a lot of the > things I asked about could be irrelevant in the limited context of > CFileSpec itself - but if they're not handled there, then where? > And I thank you a great deal for the feedback. The next draft will be much improved for it. And if you didn't have a good idea as to where CFileSpec fit into the scheme of things, then its my bad for not writing it out clearer in the first place :) -Jeremy Parsons |
From: Woody Z. I. <woo...@sb...> - 2003-01-24 18:08:17
|
class ActionFlags encapsulates a single tick's inputs for a single player. The interface (IActionFlags, apparently) provides routines to get and set various information about the input and its representational status and abilities (SetPrimaryTriggerEngaged(bool), bool DoesProvideDeltaYaw(), GetDeltaYaw(), etc.). Subclasses know how to pack/unpack themselves, how to copy themselves (either in new dynamically-allocated storage or into existing storage), and how to predict what the next tick should look like given some tick's data. (This last bit would be used for network "smearing" as well as eventual prediction.) And I suppose it would be helpful if the ActionFlags could provide an ActionFlags object representing the current input state. :) (It could use one or more other objects to help it do this of course, so that the code for testing whether a key is pressed on all the various platforms does not need to be duplicated in every ActionFlags class, etc.) This encapsulation provides the following benefits: + The packed format (or formats, if external Packers are used) of ActionFlags is free to change independent of other usage. + The internal (unpacked) representation of ActionFlags is free to change, perhaps in support of the following... + The information carried by ActionFlags can be changed or extended. (e.g. one could add DoesProvideDeltaForward() in support of using proportional joysticks to walk around, or GetJumpKeyEngaged(), or SetTypedTerminalKey(), providing default implementations in a base class (e.g. returning false for 'DoesProvide...' or 'GetJumpKeyEngaged()', doing nothing for SetTypedTerminalKey(), etc.) so games that use an older ActionFlags class are shielded from the changes). A specific ActionFlags subclass should also be able to provide a "no action" Flags and possibly a (consistent) "invalid" Flags. When packing or unpacking, an ActionFlags object should have a "previous ActionFlags" object available to it so that it can apply some sort of differential compression etc. (Maybe there should be a separate but related ActionFlagsPacker that's responsible for tracking this sort of state.) It would be nice if the ActionFlags class could help the configure-controllers UI figure out what controls should be configured and what type of input they expect (boolean or numeric, etc.). If the frequent use of virtual functions instead of inline code makes anyone queasy, refer to my previous post about optionally selecting a specific subclass at build-time rather than at runtime. A factory somewhere would determine the actual C++ class type used for a given game's ActionFlags. In my current thinking, a given game uses the same such class throughout - i.e. a queue of ActionFlags has all CMarathonActionFlags elements, never a CMyCustomActionFlags element - or vice-versa. This could be enforced by having an ActionFlags queue type that knows what type should be used (probably via instantiating a template class); any needed queues would be provided by the same factory that provides flags. I suppose the actual class used would be determined by: the film file if playing back a film, the current game personality if playing a single-player game, and the negotiated least-common-denominator of game personalities in use in a netgame. Or, maybe the gatherer of a netgame specifies the class (directly or indirectly) and only joiners that support it will be able to play. Queues of ActionFlags support peek(index) to retrieve the head element or some element beyond the head (leaving the queue state unchanged), dequeue() which is strictly for removal (thus working around potential problems with storage invalidation), enqueue(flags) which copies the flags into the queue, count() which gets the number of elements currently in the queue, maxcount() which gets the largest number of elements that could ever be in the queue, and available() which gets the number of elements that could be enqueued right now without problems. Queues also have a 'hook' or 'trigger' so that some routine can be called whenever a new ActionFlags is enqueued (and some other routine when dequeued). Maybe these triggers would have a 'threshhold' and so would only call the routine when the queue gets sufficiently empty or full (which may be especially useful for film-related queues - though maybe it's not such a good idea anyway if different threads will be the enqueuers or dequeuers in different circumstances). Queues would likely be implemented as they are now - as circular queues - to control memory allocation and to simplify communication between a pair of threads. (Note that one thread must be consistently the 'enqueuer' and one (potentially different) thread consistently the 'peeker'/'dequeuer' - or else that multiple threads trying to work the same half mediate their access on a mutex or something - for this to work safely.) Resetting a queue is semantically equivalent to dequeueing all its elements, and thus is subject to the "thread-safety" rules for dequeuers. (An enqueuer may not reset a queue, unless it behaves as a dequeuer also, taking a mutex on dequeueing as necessary if it's not the only dequeuer, etc.) The basic structure of the input system would stand as it is: Input Capturer (in Input Capture thread) periodically enqueues onto LocalPlayerQueue Network code (either in its own Network thread or in the Input Capture thread as part of a 'hook' - but only one or the other on a given machine) uses info from the LocalPlayerQueue to pack data into packets it sends to other machines. Network code (in some Network thread or in the Game Update Thread, but only one or the other on a given machine) unpacks received network data onto various PlayerQueues. It may also enqueue ActionFlags onto the RecordingQueues. Game update code (in the Game Update thread) uses data from the PlayerQueues for updating the players' states. (Maybe it eventually builds PredictiveQueues, or something, internally...) Recording code (in some thread or another) takes data from the RecordingQueues and packs it out to a file (maybe using some of that differential compression hinted at above, e.g. to do RLE as the current code does...) In a single-player game, flags go from the Input Capturer directly into the PlayerQueue for the local player. In film playback, flags are unpacked (in some thread or another) from the film file into the PlayerQueues. So these elements should all be configurable with "what queue should I read from" and "what queue should I write to"; when starting a netgame or a single-player game or a film replay etc. they're all wired up the way they ought to be. There's a master clock somewhere that helps the world-updater figure out when it should actually use flags (it may have more in its queue than it should consume at the moment). In the current M2/A1 implementation this is driven by the Input Capturer and is called either heartbeat_count or "net time". As with the queues, the world-updater should be configurable with what master clock to use. All this decoupling and wiring-up stuff helps make the systems more independent and thus more modular and flexible. In the future some other code may want to route things another way; it does this just by wiring things up differently at initialization-time. There's no need for it to go through and change the Input Capturer to check whether the new configuration should be used and, if so, send flags elsewhere; Input Capturer stays just the way it is but is wired up differently when the new game starts up. There could be additional Queue types that don't really queue things... e.g. one "fan-out" that takes flags enqueued to it and enqueues a copy of them on each of several queues it's wired to, another "overlay" type that pulls from the highest-priority queue if there's something there, discarding the corresponding flags from all lower-priority queues, or else pulling from a lower-priority queue if the highest-priority queue is empty, etc. Random note that didn't fit anywhere else: currently the Input Capturer code (parse_keymap() at the moment, basically, I think) must be safe to run in both the input-capture thread (for normal use) and in the network thread (in case the network code needs a "smear"). Using mostRecentActionFlags->GetPredictedNextFlags() for smearing, instead of tripping back to to the Input Capturer directly, makes the scheduling behavior a little simpler and thus probably a little safer. Woody |