Jump to content

Mumbles

Members
  • Posts

    691
  • Joined

  • Last visited

Blog Entries posted by Mumbles

  1. Mumbles
    Online games... The unsavoury types are always drawn to them. As a game admin, you simply kick them out of your game, or ban them if you're in such a mood and that's the end of it. But now looking at it from the developer side of things, I can't help thinking a bit more about it.
     
    It's one of UDP's advantages over TCP. When you get a known trouble maker trying to join your game, your server can either tell them to sod off (in the nicest way possible), or it could pretend that it never even got the request. Easy.
     
    What about when you have TCP, or both? TCP can't just "ignore" a connection request* - it's just not allowed. All you can do is either call accept, which will (normally) block until a connection request actually arrives -- or not, in which case, no one can connect, not even the good guys... It's something most people here won't need to think too hard about, but perhaps a bit if input for those who it will affect (namely: me)
     
     
    Most games using a TCP connection system, will continually call the accept function, since it wouldn't be much of a multiplayer game if only the host could play. When a known trouble maker comes along, the server simply closes its side of the connection and refuses to send anymore data. The trouble I learned recently is that you can't actually force the remote peer to close their end of the connection - and you can't delete your socket handle until they do shut the connection, in case they are still sending data to you...
     
    Now I know that winsock isn't really built for games. For starter's Nagle's Algorithm starts enabled, and it's highly recommended in the MSDN that you leave it enabled. Yes, it's easy to disable (setsockopt with TCP_NODELAY set to false), but the fact you have to manually disable it at all, says it's not what winsock is really targetted towards.
     
     
    So, already I've realised that once an undesirable gets in, I could in theory have them wasting a valid socket resource (if they're not using an official client), by refusing to shut the connection and waiting for the timeout to occur. By recently I found a way round this... Winsock allows something called "conditional accept" which is a callback called by the accept function, where you can look at the sender's IP and port (although the port isn't really helpful at this stage) and decide whether to open the connection, or to refuse it - Thereby causing the socket to get error 10061, as if the server application wasn't running.
     
    A little more reading though, and I discover that this conditional accept thing is on the list of "Lamest things supported on win32". Without conditional accept, as soon as a connection request arrives, it's accepted automatically in kernel space, so it's about as fast as it can be, whereas the conditional accept callback is done in user space, and switching between them many times a second takes time. The argument is, that it would be so slow that genuine users can be getting 10060 timed out errors due to the accept not being handled fast enough.
     
    Anyone here know anything about kernel stuff? I know nothing about it, but when I asked my brother, his response was "anyone who thinks that switching between kernel space and user space is slow, probably thinks a kernel has something to do with fried chicken". He also suggested that even with several hundred users, a well optimised system would see no slow down in accepting new users via this "slow" callback, and that it would probably only fall over during a SYN-flood (but not using the callback is just as vulnerable).
     
     
    Personally, I can't see why other TCP games haven't used conditional accepting. It's been around for years, so professional companies must think there's something wrong with it. But what? It seems like such an obvious solution. Why do we need to say: "no, you're not allowed in... Go away please" when we could simply pretend there's no one home (Same as they can do on UDP)?
     
     
    *Actually TCP can ignore connection requests. During the conditional accept callback simply don't return a known value (CF_ACCEPT or CF_REJECT) and your socket will get error 10061 (rebind the socket and then you can carry on using it) but their socket will get 10060, as if the server wasn't even switched on... However, it's a cheap hack and probably not a good idea to use it, since this is undocumented behaviour.
  2. Mumbles
    It's something I've always been opposed to - creating a class, knowing full well I'll only create one instance of that class, or none at all. I've always been an advocate of the idea "only create classes if you want to create several instances of the class". It's what always kept me away from the idea of using LEO, instead of using the plain C interface.
     
    How many "engines" are going to be running inside my game? Just the one, so why make an engine class? Despite saying this, for years I did have some of my own 1-instance classes - usually some sort of linked list (I know these exist in the STL libraries but I'm a bit of a nutter and like to reinvent even basic things). I'd set them up as classes because I wished to hide some data, such as the pointer to the head of the list, so that it was always valid and could never be tampered with accidentally. The private access specifier was great for this, and anything else I never wanted to call/access accidentally.
     
    Sadly, these private members always showed up in intellisense and it even offered to autocomplete them for me, only to then tell me that I was accessing a member function that was out of scope. Not hugely serious, as it was only a quick few seconds to replace the bad function call with the right one, but a little irritating nonetheless.
     
     
    The lergest problem I was running into was that I noticed I lacked consistency. Half of these list classes I'd designed with the intention of being "static classes" and the other half expected an instance. The was no compelling reason to have chosen either, and was probably determined each time by how much stamp glue I'd been sniffing in the previous hours. I turned to Google in an attempt to standardise my classes and find out in no uncertain terms which was the better way, and then update my classes to reflect this.
     
    I soon found though, that really, in the land of C++, neither was really the right option. Static classes were a sort of "hack" solution for Java (which is what we were taught at my uni) to achieve the same thing that C++ achieves with anonymous namespaces. Well, I call them anonymous namespaces because it sounds fancier than unnamed namespaces which is their correct name.
     
    Indeed, I'd always known that namespaces, didn't actually have to have a name (even though the word namespace kind of implies it does), but I'd never seen any advantage of them. Since discovering how to use them "properly", I sort of couldn't keep it to myself, so hopefully any other C++ users out there who thought the same as I once did, could learn something here.
     
    Here's one of my list classes (header only), as a list. It's one of the simpler lists, which much more resembles an STL linked list, than some of my others do
     

    class RaysList { public: RaysList(); ~RaysList(); void AddProjectile(RayObject * RayToAdd); void DeleteProjectile(RayObject * RayToRemove); void DeleteExpiredProjectiles(void); void DeleteAllProjectiles(void); RayObject * GetNextProjectile(void); RayObject * GetCurrentProjectile(void); bool EOL(void); void ToStart(void); bool IsEmpty(void); private: struct ProjectileListEntry { RayObject * AssociatedProjectile; ProjectileListEntry * NextListEntry; }; ProjectileListEntry * ActiveProjectileHead; ProjectileListEntry * MemToReadFrom; };
     
    This one as it happens, has no statics (decide for yourself how much glue was involved beforehand), and it looks simple enough.

    There's a constructor to set the two private pointers so they're never pointing to invalid memory (0 being hardcoded to mean end of list, thus is valid)
    There's a destructor to free any pointers that were allocated, should the list fall out of scope
    The member functions are all public so they can all be called validly

     
    But it can be re-written without a class, whilst still keeping the access specifiers. "How?" You might ask, because it's true that public, protected and private (well, and friend too) are all class access specifiers - they can't be used for structs or namespaces. Well, here's how:
     
    (Header first)
     

    namespace RaysList { void AddProjectile(RayObject * RayToAdd); void DeleteProjectile(RayObject * RayToRemove); void DeleteExpiredProjectiles(void); void DeleteAllProjectiles(void); RayObject * GetNextProjectile(void); RayObject * GetCurrentProjectile(void); bool EOL(void); void ToStart(void); bool IsEmpty(void); }
     
    No pointers at all here, but somehow my functions are going to return some. The magic takes place in the definitions (the .cpp file)
     

    namespace RaysList { namespace { struct ProjectileListEntry { RayObject * AssociatedProjectile; ProjectileListEntry * NextListEntry; }; ProjectileListEntry * ActiveProjectileHead = 0; ProjectileListEntry * MemToReadFrom = 0; } void AddProjectile(RayObject * RayToAdd) { ProjectileListEntry * NewEntry = (ProjectileListEntry *) malloc (sizeof(ProjectileListEntry)); NewEntry->NextListEntry = ActiveProjectileHead; NewEntry->AssociatedProjectile = RayToAdd; ActiveProjectileHead = NewEntry; } }
     
    The rest of the functions haven't been included, but for this demonstration they don't need to be, this one function shows you how it works.
     

    At the top of the file (not shown) is a line to include the header file
    Again, we open a namespace with the same name, but immediately inside this namespace, we create another one, without a name
    Anything inside the RaysList namespace has total unrestricted access to the secret namespace
    So, because of this, the pointer declaration to the secret struct in the first line of AddProjectile() is valid
    But if were to try and declare a pointer to this struct from outside the namespace it wouldn't be valid, and it wouldn't even show up in intellisense, so you wouldn't even be tempted.

     
    Even the lack of a destructor doesn't cause a problem, since the pointer variables are declared outside of functions, thus they never fall out of scope... So you don't have any memory leaking.
     
    The only reason we might have wanted a class for this concept... Eliminated... I'll never create a "static class" ever again
     
    So there you have it. If you want the same effect as the private access modifier, but don't need to create an entire object, there's a way.
     
    Sadly, because I learned Java at uni, I always thought this should have been possible, but never knew how, because of course, in Java, everything is an object, even if you're only going to create one instance of a class.
  3. Mumbles
    That might be a question going through some people's minds. The answer is "yes" I'm still working on it. Just much slower than before.
     
    At the start of the project everything was working brilliantly, I'd got as far as adding an external newton into the project. I'd even got it to correctly build the physics shapes from wavefront obj files on-the-fly, and force and torque was all working too. So where did progress suddenly freeze?
     
    The answer was the player controller. I would have to write my own so that it could be processed by a dedicated without the leadwerks graphics. Problems keep coming in when I find that I've built something that ends up needing entities. Because of course, if you have not called Graphics() and CreateWorld() you can't create entities... So far I've been able to work around, but anyone who's active on the boards will have noticed my most recent problem, posted as a status message last night.
     
    ...The problem now is that Newton always adds forces in global space. So when I want to move my player forward, I have to transform a Vec3 of (0.0f,0.0f,1.0f) into whatever the correct vector would be for the angle the entity is facing. There is the problem, TFormVector's second and third parameters are entities, so graphics needed. Josh came to the rescue with some source code in an email. I believe it's meant to be private, so I won't post it here. The problem is it's BlitzMax code, so it doesn't translate exactly.
     

    The code has a TMat4 data type, but my version of the engine does not. From reading the wiki, I have changed these to TVec16 but then there's another problem
    It does something like this:
    TVec16 TempMatrix; TempMatrix.inverse();

    TVec16 in the C interface does not have an inverse function, and I can't seem to find command in the wiki that would do this. In fact, if you press the "dot" key after a declared matrix, visual studio will show you 16 fields: A0, A1, A2, A3, B0, B1, B2, B3, C0, C1, C2, C3, D0, D1, D2, D3 ... A TVec16 does not seem to have any other members that I can find.

     
    The other problem is that I can't build an inverse function myself because I have no idea what it is supposed to do. One thing about me is that I did not do A-level maths. My uni course covered matrices very briefly. We were taught, as an example "Here are two matrices -> this is how you multiply them together" . That much, I can do, but we were never shown what they might actually be used for. I have no idea what the inverse of a matrix is... Reverse the order? Flip the rows and the columns with each other? I just don't know.
     
    I would ask Josh, but he has already provided enough help, even when he didn't have to. I'm sure he's busy enough with the next version of the engine, that he doesn't really have time to spoon feed what is probably basic level stuff, and so it would be just plain wrong (and selfish) to ask ('nag' may be a more appropriate word).
     
    It's depressing stuff, which has brought the development speed of my project almost to a complete stop. But I've not totally given up on the project... Not yet.
  4. Mumbles
    A good question, one that it seems several people have now asked me. The answer is, I'm doing quite a few things, and multi-tasking them. Of course this causes a problem in that it can appear to take much longer to get any visible results down, but I remain faithful. During my last year at uni I was tasked with making a simple multiplayer game, and only in the final two months before submission could you really see any evidence of the game itself. But once the first sign of life had emerged from the project it made huge progress in a short time. So I'm not yet worried that my Leadwerks projects are not really in any playable state. But people will be asking, "just what are these projects that you are working on?" I'll explain:
     
    The first project, which currently gets most of my attention is a multiplayer arena style game, think Quake 3 but with different maps, powerups and weapons. The idea is that this game will provide a foundation to build a more complex FPS game. For the second one, think Mafia and GTA: Admittedly, more like Mafia than GTA, more of a linear story as is the case with the first mafia game (although the upcoming release of mafia 2 might provide some interesting inspirations). Should be quite an interesting project since I don't really know too much about that style of life. Hopefully it will provide some comical stereotypes, but so early on, it's too early to make such a promise. Those are the brief abstracts, more about the second game when the first is ready.
     
    So, this Arena game of mine (or id software's, which may be more appropriate), what exactly is it going to involve? Well anyone who's played Quake 3 probably knows that deep down, the game is not actually that complicated. There are nine unique weapons which all have their own strengths and weaknesses. And the simple objective would be to score the most number of frags within a specified timeframe. During that time, you would decide which weapon is most suitable to use for that task at any given time, whilst making sure that you are strong enough to survive an attack from another like-minded player. Powerups occasionally appear in the arena to make your job a little bit easier, again, they all help you in their own way.
     
    Well, I don't see too much change in any of those mechanics, but implementing those are some of the core elements of any shooter game, so getting them down once would make a larger-scale game much easier to develop, or at least that's the idea. The biggest difficulty is going to be controlling the players. I could not decide whether it would be easier to code the network side of the game first, or the single player bots, and each have their own set of challenges to the developer:
     
    Networking involves:
    Tracking the connections and disconnections
    Handling the client input, and ensuring all other clients update the associated player model accordingly
    Attempting to hide any network delays, because, best intentions in the world, problems do happen, and warping clients to old positions is annoying. More so when it happens frequently
    Attempting to keep the client's view smooth. It may be great if you don't get warped around all the time, but try aiming at another player who is experiencing this problem. Also, keeping prediction reasonable. Attempting to remove any over-predictions, which cause many (visible) corrections to be made
    And I'm sure there are more that I'll experience during development
     
    Single player bots involve:
    Knowing the arena, and where to stand. isn't is just silly if a bot walks somewhere it shouldn't (Like Q3's red mist, or the void of space). Sometimes the most direct route is not always the best, so this involves pathfinding in some way
    Being able to 'think'. Walking around randomly may be enough to start out with, but really, wouldn't it be so much more interesting if the bot knew to run for some health when it was weak (instead of perhaps running to grab a railgun)?
    Being able to 'lead-aim'. We all know that rocket launchers sterotypically do not hit instantly. To prevent their explosive blast being over powered, they take a while to reach the target. This allows an observant player to attempt evasive action. But the bot has to take this projectile speed into account as well, as simply firing where the target was is unlikely to work. For the case of grenade launcher, the influence of gravity must also be taken into consideration when using the weapon offensively (unless the bot chooses to use another weapon that is)
    And possibly, if I really feel like it, attempting to predict the player's movement over somewhat long periods. Example: the bot has done a tremendous amount of damage to another player. It's likely that this other player will be going to the megahealth spawn, and praying it's there. Does this bot know a faster way there? Simply chasing will guarantee the player will get the mega health (if it's available), whereas taking a faster route may deny the player the health (and it's even possible that the bot can grab it instead). That's a difficult one, and so will be attempted much later.
     
     
     
    Eventually, I decided I would go with the network side first. After all, at least then people could play the game with each other and have longer lasting fun that with poorly programmed bots. Also, the bot's play style could be influenced by watching people playing the game.
     
    Building the bots will require a pathfinding system. I know most people here all seem to have taken a liking to A-star, but I won't be going for that, instead, think of a breadth-first search system, but with some minor changes, giving some features of a uniform cost search. When I've actually built this system, I will explain just how it works. All I will say is that unless I've really got something very badly wrong, it should -always- find the fastest path from start to destination. without being too slow, but of course, time will tell...
     
    Closer to time, I may be asking for some media input from the community, models in particular. But I will not be asking for such input until the programming is close to finished, and will be using my own (sub-par) milkshape efforts until then.
     
    Once the game is finally finished, I'll walkthrough exactly how I created the game so that others can have something to learn from if they're not quite sure how to build such a game. I'm now definately committed, this game will -not- be using 2.3 or newer. That does not mean that it would be of no use to read through the source code though. The mafia-like game will probably be an LE 3.0 game.
     
     
    So like I said early on, there's no signs of life yet, but when screenshots come up, you may be surprised how quickly it all takes shape
  5. Mumbles
    I'm not shy about the fact that I always thought 60 was completely ridiculous number. Building something that I hope to actually be finished one day, WITH multiplayer capabilities, I thought 50 would be a better number, as it's much more scalable (every 20 milliseconds as opposed to every 16.666667, Or 17, 17, 16, 17, 17, 16 etc depending if you use floats or ints).
     
     
    But looking at commercial game developers, they think even that is too high. I've sort of got raycast projectiles working (I say sort of, because I can't see them breaking at present), and of course for "moving projectiles", the length of the ray (which translates to how fast it travels) is affected by the number of physics steps per second:
     

    100 steps per second, ray is one unit long
    50 steps per second, ray is two units long
    30 steps per second, ray is 3.333333 units long
    etc

     
     
    But what about player movement? There's no raycasts involved here. Players are represented using ellipsoids that are stretched tall. They are actual physics bodies that are kept upright using Newton's custom 6DOF joint (which there is no access to in LE2, but I've made them more or less the same as upvectors, which are very similar to LE2's raycast player controllers). All rotation is restricted by the joint, which prevents them from toppling over but they also can't "look to the side" for example (your camera can as its rotation is not physics based at all, it just has its position locked to your player body).
     
    I set about updating the physics update section of the main loop, which is rather small. Once every (1000 / tick rate) milliseconds (based upon the time of the first ever tick, not the time of the previous tick), the world is updates by (1 / tick rate) seconds. So if tick rate is 50, then once every 20 milliseconds, the world updates by 0.02 seconds.
     
    I swapped the tick rate number out for 40 instead of 50. It's still divisible by 1000, but now the world will advance by 0.025 seconds once every 25 milliseconds rather than the old 0.02 seconds every 20 milliseconds. That sounds right doesn't it?
     
    The first problem to arise from this, I'd already expected. I hadn't updated the amount of force that each player moved by. This was deliberate. If the force value was a number per second, than the code wouldn't need updating. But it wasn't - it's just an arbitrary number. So the next step would be to calculate the force required, such that were it was 1 before, it would now be 1.2. I preferred a calculation over the fixed number version because once I had got the same distance per second working at 40 ticks per second, I would then change it again to see if it still worked.
     
     
    There is however, a complication. A force of 1.2, 40 times per second, does not give the same results as 1.0, 50 times per second. The most logical reason I can think of because once the next physics step arrives, I add a new 1.2 to whatever force was still acting on the body from the previous update. This in theory should be fine if there was no cap on the maximum speed a player is allowed to travel at. If you add forces on the bodies for however many ticks 2 exact seconds works out as, and then let them slow down naturally, they should stop at the same position. But I've imposed a maximum running speed cap to prevent players from speeding up like motorbikes and reaching silly speeds.
     
     
    I played around for a few hours trying to come up with some exponential formula to allow deceleration to occur at the same rate, regardless of the update rate, but without removing the speed cap, it just wasn't going to work (and with the speed cap removed, the complex formula shouldn't be necessary at all). Eventually I tried a different idea.
     
     
    Instead of trying to change how many times per second the physics are updated, why didn't I keep that constant along with the forces, and instead only change how often the forces were calculated? To make this idea work I'd have to remove the current system and replace it with a new one - one that ticked much, much faster - insane speeds by comparison. Well that's exactly what I did. There were 1000 physics updates performed per second. (you read that right, but I'll write it out in words just to make it absolutely clear, one thousand times per second). I know what you're thinking: "you think you can make a game playable over the Internet when it's updating 1000 times per second... You're mad!"
     
    I believe Einstein went mad too, but this isn't mad. I'm thinking along the idea of 1000 little steps per second. For a case of 50 ticks per second, we calculate "movement" forces, and then for the next 20 ticks we keep giving the objects the same movement forces. If our force is equivalent to 0.1 units per physics mini-step, then after 1 second we will have moved 100 units if we kept the movement key held down for the entire time. The same is visible if we calculate the forces 40 times per second instead - there's still 1000 mini-steps occurring each second, it's just that the forces are being calculated less regularly, but if you don't alter your movement vector, the result is identical.
     
     
    It means that now, the I can set the world's apparent tick rate based on how the game is being played.
    Over the Internet, 25 seems like a good number here, and for local network play, 40 is probably plenty fast enough (For reference, Unreal Tournament uses 20 and 35 respectively, but I thought 35 wasn't very scalable). For single player games, it depends on the style of game here, but for a FPS, I don't 50 (or even 100) would be excessive, however I feel multiplayer co-op story based games should probably stick 40. There's a lot of information to keep synchronised there, and it gets worse when there are more than 2 players involved.
     
     
    However, things to consider if using the mini-steps idea. "external" forces, such as being sent flying by a rocket explosion should only be applied for 1 tick. The explosion only happens once - it's not a persistent force like walking would be. Keep your internal and external forces separate - add them both during a mini-step, but afterwards, clear the external forces, but keep applying the same (not recalculated) internal forces until your next "major" step.
     
    It makes no difference if (for example at 40 ticks per second), you really do update the world once per millisecond, or if you simply call the update function 25 times in a loop, and then not perform any more mini-steps for another 25 milliseconds. Using the latter method may help to remove the temptation to recalculate forces before the next major step, and closer resembles the layout of our 40 ticks per second behaviour. It also means your main game thread can sleep, thus leaving your client threads with more CPU time to accept your clients' (or AI bots) inputs.
     
    In a very large world with many bodies, 100 mini-steps per second may be more preferable than 1000, but there will be some loss of accuracy here, or you'll need a system to know when to add an extra step. After all, now each major step will occur after 2.5 mini-steps (in case of 40 per second), so you'll need to keep track of when to add one extra. It's easy for 40, all major steps have two mini steps, but every second major step will need one extra - but if you want 17 (just as a deliberately awkward example) major steps per second, that will be a little trickier, although not impossible.
×
×
  • Create New...