In top down design we decide what the end product is to be and then break it into a set of small easy to understand, bite sized pieces. The first step is of course to figure out what Yerushalem is going to do. Next the project is subdivided into broad areas such as user interface, networking, and etcetera. Thought is given to how all these interact. Then each is digested into smaller and smaller units until we reach the level of each unit representing a C++ method. At various points along the way, depending on how large a unit is, units get delegated out to others to flesh out.
This section is for fleshing out what we want Yerushalem to do.
This is for nebulous, general goals.
This is where easy to define goals go.
This section pins down what we need in order to meet the goals above.
This section pins down how we meet our requirments.
YERUSHALEM game-structure ======== CONTENTS ======== 1. STRUCTURAL OVERVIEW 1.1 SERVER (sv) 1.2 CLIENT (cg) 1.3 WORLD (world) 1.4 sv/cg CONNECTION (ServerCon / ClientCon) 1. STRUCTURAL OVERVIEW ======================= There are over all 4 classes that interact with each other: CLIENT is the client interface SERVER is the server that handles all client-interactions and sends them to the clients and his WORLD. sending data is done through the CONNECTION, where the client only has a single one, while the server has one for each client. this structure makes it possible to have a client and a server running simultaneously on one machine: the connection will not be a network-connection but possibly something like a simple socket or even just a function-call. the world will be the same for both sides. because of this the client has to check if he is a pure client or a server-client-combo. if he is a combo, he possibly won't be allowed to call the world to update itself (as the server does this) DATA FLOW ========= CLIENTS SIDE SERVERS SIDE ---------- ---------- | CLIENT | XXXXXXXXXXXXXXXXXXXXXXXXX | SERVER | ------------------------------------------------------------------ | X sv/cg CONNECTION X | ----------------------------------------------------------------- | WORLD | XXXXXXXXXXXXXXXXXXXXXXXXX | WORLD | --------- --------- data will flow - - from clients to the server (containing all clients interactions) - from the server to the servers world (changing the world due to clients interactions - from the servers world to the clients world (updating the clients status or requesting new sector-data) - from the clients world to the client (e.g. when looking which interactions are possible) - from the server to the client (global messages (chatting) may be exchanged that way) 1.1 SERVER (sv) the main server-loop will look something like this { - retrieve player-state information from clients - take care for players that want to (dis)connect. - call the world so it cares for itself - loop through all DYNAMIC ENTITIES 'ent' { - look if ent needs to change due to a client-event, timer or other events (!! BUT NOT DUE TO SPEED OR FORCE as the world does this itself !!) if so, change its status and mark it. } - all marked objects` status need to be send to the clients. (in form of a 'game-snapshot' which would be a complete status - or in form of single entity-specific information) - if there is a request to stop the game, break. } for easier handling lets call one iteration of this loop a single cycle. be aware that events happening in the same cycle will possibly not affect each other the way we think, as we go through the virtual world entity for entity. interactions of separate events will possibly need a multiple of 1 cycle. but that shouldn't be too bad, as in one second the server should (hopefully) calculate hundreds of cycles. 1.2 CLIENT (cg) due to the event-driven character of the engine, the client-structure needs to look different: when using CS to render grafics, we need to enter the csDefaultRunLoop(). due to this, we have to use the EventHandler. ============================================================================================= bool Client::csEventHandler(iEvent& ev) /** this function is our major entry from CS * for a list of all predefined events, look CS/include/iutil/evdefs.h * for a little description on event, look the CS/docs/html * especially 5.2.4, which talks about cscmd***Process */ { if((ev.Type == csevKeyDown) || (ev.Type == csevKeyUp)) { /// here we can easily handle keypresses and /// possibly UI-events self.ProcessClientInteractions(); return true; } else if(ev.Type == csevBroadcast) /// cscmdXX are broadcasted if(ev.Command.Code == cscmdPreProcess) { /// here we can communicate with the sv Connection.Interact() /// or something like that /// and ask the world to recalculate itself World.Calc(); return true; } else if(ev.Command.Code == cscmdProcess) { Self.SetupFrame(); return true; } else if(ev.Command.Code == cscmdPostProcess) { /// add a HUD onto the image self.DrawHUD(); return true; } else if(ev.Command.Code == cscmdFinalProcess) { self.FinishFrame(); return true; } else return false; /// event wasn't handled } ============================================================================================= someone may argue that if the client is structured this event-driven way, it may also be better to structure the server that way too. i'm not sure, yet, but i think we can save at least a bit of overhead that way. i am also not sure if we must use the csDefaultRunLoop() to work with registry and other CS-internal data. if so, we must use the event-handler on the servers side, too. 1.3 WORLD (world) as on both sides (the server and the client-side) movements and the effect of forces can be calculated, we can do it on both sides to reduce network-traffic and packet-parsing. WORLD will be a class holding all entities. we can easily replace the above mentioned tree of all entities with the world. furthermore the world will calculate continuous changes of itself (like a bullet flying with a constant speed in a constant direction). WORLD will do a lot of things related to CS. 1.4 sv/cg CONNECTION (ServerCon / ClientCon) the CONNECTION will take care for all interactions between the client and the server. it will possibly split into a ClientConnection (the server may hold one for each client) and a ServerConnection (one on each clients side). YERUSHALEM client/server architecture ======== CONTENTS ======== 1.0 SECTOR-USAGE TO INCREASE PERFORMANCE 2.0 DISTRIBUTED SERVER NETWORKS 1.0 SECTOR-USAGE TO INCREASE PERFORMANCE ======================================== with CS a level is organized into sectors, which will make our work much easier. a server (still need to think about a multi-server-network) will know the complete world, while a client only needs to know about all entities in its immediate area. so lets say client enters game and starts in sector F6: (a 8x8 2-dimensional matrix) a b c d e f g h 8 . . . . . . . . 7 . . . . . . . . 6 . . . . . c . . 5 . . . . . . . . 4 . . . . . . . . 3 . . . . . . . . 2 . . . . . . . . 1 . . . . . . . . distance is the number of portals between two sectors depth(n) is 0<=distance<=n. Or a distance between 0 and n inclusive. sector_sum(n) is the sum of sectors reachable within some depth n // much better, greg. thx with a depth=1 he will only need to know about sectorsum(1) == e7,f7,g7,e6,f6,... with a depth=2 he will also need to know about sectors e8,f8,h8,.. the higher the depth, the more information need to be exchanged with the server and the more things need to be rendered keeping the depth low will reduce network-traffic and increase rendering performance. BUT: for a small depth: the smaller the depth, the less realistic the game will be, as events happening in the other sectors (like sounds, explosions, quakes whatever) will not affect the player if the distance is too high. so sector-width and/or depth should be chosen according to this. /* Gregged. Perhaps depths could vary according to event type? For example an earth quake may need to be distributed across many sectors, while a conversation between two people would possibly stay in the sector of origin. Maybe the event object could have a varible associated with it. As the event passes from sector(or server?) to sector(or server) the var--'s. Once var==0 it dies on it's server. */ /* fleshyCPU first i thought a client connected to a server would request data of specific sectors, but that doesn't actually make a lot of sence. you are right. it will be much more effective if the server sends event-data according to the position of the clients. /* 2.0 DISTRIBUTED SERVER NETWORKS =============================== possibly this will also be the key to a distributed server-network: a b c d e f g h 8 a a a a b b b b 7 a a a a b b b b 6 a a a a b b b b 5 a a a a b b b b 4 c c c c d d d d 3 c c c c d d d d 2 c c c c d d d d 1 c c c c d d d d this would be a possibility for a distributed server-network. we have 8x8 sectors and 4 game-server. each of them takes care for 4x4 of them. a client only would have to contact multiple servers while being near a border. the servers themselves would only need to exchange game-wide information like the playerinfos or the global game-status (if someone has won etc) or if a single object moves from one sector into another, where another server is reliable. this would reduce traffic to a minimum. /*Gregged: I like this concept. I'd add to it the possibility to vary depth by event type (which is alluded to here) and use priority queques. */ YERUSHALEM entity architecture ======== CONTENTS ======== 1.0 INTERNAL ORGANISATION OF ENTITIES 1.1 IN CRYSTAL SPACE 1.2 OPTIMIZATION HOLDING ENTITIES IN A TREE 2.0 ENTITY PROPERTIES / FUNCTIONS 1.0 INTERNAL ORGANISATION OF ENTITIES ===================================== 1.1 IN CRYSTAL SPACE entities should be organized - as CS says - with CS-internal structures (see docs/html/* chapter 5.7.9: Attaching User Objects to CS Objects). name of the interface should be iEntity. this class iEntity could have an embedded interface iActions, that holds all actions that a player can do with it. possibly a single action would be defined something like this: struct Action_s { const char* name; const (void*)(do)(void*); } but for optimization purposes we should possibly use a global table that holds all available actions someone can to. if someone wants to act, we will look in the table, what his character can do with the specific object he wants to act on. 1.2 OPTIMIZATION HOLDING ENTITIES IN A TREE this way all objects will be in CS`s object-registry (struct iObjectRegistry) and we don't have to create our own container for them /* fleshyCPU: using this method it would be necessary to retrieve a complete list of entities each cycle and free() or DecRef() it at the end of the cycle. this is very ineffective. in the beginning we should retrieve a list from the object registry (WITHOUT o->IncRef()) and from this we build a tree sorted by sectors containing all entities. this will also help during collision detection: as we only have to check for collisions in sector-bounds we can use this reduced entity-list. when an entity is destroyed, we will remove it from our tree and o->DecRef() it out of the object-registry. another question is: should we handle static and dynamic entities in the same way? (static entities will be things that can not change during the complete game, like walls etc [destroying walls can be done using portals] ; dynamic entities are objects that may interact). at the second level of the tree we could split between dynamic and static entities and thus when iterating through all entities we only need to check the dynamic ones. */ 2.0 ENTITY PROPERTIES / FUNCTIONS ================================= specific objects like the players should be able to 'hold' or contain other objects. for example a player can hold a weapon. the player could have a specific ability to hold a special number or special 'weight' of objects. should we take care of objects specially-believing people would't touch/carry? /*Gregged: If an object can be touched or can be carried, it should be possible. It could become a memory problem though. Imagine this situation: Player throws rock at glass window. Window shatters, glass shards fall to ground. Player picks up a shard and slashes another player's throat. Now perhaps we could make the window a fairly normal CS thing. But when player hits it with rock, the engine reads additional properties from a file, finds out glass shatters, that it makes a usable weapon (although cuts into users hand) etc. Once the glass is no longer being used, these extra properties are freed from RAM. */ /* fleshyCPU such an object could contain the action 'break', which would window->DecRef() (destroy the window) and spawn() new glass falling down, destroying itself after a period of time being not used. once it was used this timer should be reactivated when the user drops it. this glass needs to be of different material, though, as it needs further actions like 'useShortRangeWeapon' 'useThrow' etc, while the former material/glass would only have a single action 'useBreak' or 'useDestroy'. */
skeleton-based movent for a description of the human skeleton, see various books or arch.skeleton.gif (one of you english guys may write down the english names for the various bones). if we want that our characters perform realistic movements, we can choose between different levels of authenticity: the less realistic, the higher will the performance be. -the most realistic version one can choose is to simulate every bone and every muscle, but thats of course impossible for us. but this version gives us the possibility to create descriptive bodies, we could generate skeletons based upon descriptions like "20 year old 1.80 meter high, male, ..." -the most efficient would be to use animated models, as games like quake and half-life and most others do. but using this way we would have to generate different models for different looking people (except the skinning) i don't know if this will be possibly with the performace someone may have: we will reduce the skeleton to a number of important parts and create basic physical formulas to set up the correlation between them. using these formulas we can calculate movements and animations. a client 'C' will possibly consinst of an entity 'CE'. each bone-part B may be an entity 'BE' itself, as a child of the client: CE { BE (skull) BE (arm) BE (leg-bone) ... } for animation the client may have a seperate embedded interface iMovements (or iAnimations or...) which will hold functions of different complexity: go (direction, distance) step (direction, length) move_lower_leg (angle) move_higher_leg (angle) move_foot (angle) ... this way we can simulate a lot of activities, like a person just going somewhere or kicks, grabbing a thing, interactions between people (like: one person giving a thing to anotherone, as the hand may hold an entity). furthermore we can use realistic collision-detection for the body and create hit-zones. the question then is: how do we wrap the skin above these entities? hopefully someone of you has an idea.