Sergey Ignatchenko

GDNet+ Basic
  • Content count

    67
  • Joined

  • Last visited

  • Days Won

    1

Sergey Ignatchenko last won the day on September 18

Sergey Ignatchenko had the most liked content!

Community Reputation

625 Good

About Sergey Ignatchenko

  • Rank
    Member

Personal Information

  • Interests
    DevOps
    Programming
  1. When is lag compensation needed?

    FWIW: to complement answers above, you may want to take a look at http://www.gabrielgambetta.com/client-server-game-architecture.html and my http://ithare.com/mmog-rtt-input-lag-and-how-to-mitigate-them/ (they're describing the same things - but from somewhat different angles).
  2. Stop hacking

    As for "can decompile" - yes (though more accurate would be to say "can decompile, given sufficient time"). As for "simply don't matter" - I certainly cannot agree with it if speaking about MOGs. If speaking about single-player games - the cause can be indeed lost, but with MOGs the situation is drastically different. First - for MOGs it is not about the fact that our game was broken, but about the number of cheaters (who will break the whole game ecosystem if not kept in check, scaring away honest players). Moreover, for MOGs it is not necessary to "run faster than the bear", it is enough to "run faster than the guy next to you". As a result - when dealing with cheaters, every bit counts (this includes Client-Side obscurity). Way too many serious MOGdev companies did realize it too late - and are spending LOTS of time dealing with this problem now (one publicly available example is DaysZ which even had a talk about it IIRC on GDC2016, but there are LOTS of such efforts out there). Second - for MOGs we can count on server being always-present-while-playing. This, in turn, allows to attack that "given time" clause in the "can reverse engineer" statement above. Very very roughly - we can automatically change protocols on each build - forcing attackers to break protocol obfuscation on each build; throw in intra-Client obfuscation - and it can become extremely difficult to get past the defences within the 3-4-weeks time (and after the update - the process starts pretty much from scratch). Detailed discussion of this process is going to be a central point of upcoming Vol. VIII of my "Development and Deployment of MOGs" (with chapters of "1st beta" of Vol. VIII scheduled to appear on my ithare.com in October, they should be enough to get the idea; actually - I plan to publish 20-page outline for "Bot Fighting" chapter as early as next Monday, Sep 25). Overall: - sure, Authoritative Server is a strict prerequisite for dealing with cheaters. - however, just having an Authoritative Server is NOT sufficient, and LOTS of things has to be improved on this way to deal with bots (all kinds of them, from aiming bots to grinding bots, with whatever-we-can-imagine in between). This leads us to such things as encryption (to prevent self-MITM class of attacks which facilitate fundamentally-undetectable proxy bots) - which, in turn, implies hiding a certificate within the Client (hey, this is already security by obscurity!), obfuscation of data structures within the Client to prevent bots from reading the data from RAM easily, and of course, all-popular (but not nearly as efficient-as-advertised if taken alone) "code encryption" (actually, scrambling), debug detection, etc.
  3. Algorithm Books on MMORPG engineering

    Well, my book of course :-). It is titled "Development and Deployment of Multiplayer Online Games" (planned at 9 volumes, currently Vol. I is printed, and Vol. II - Vol. III are available as early beta). E-book versions are available on Leanpub: https://leanpub.com/b/development-and-deployment-of-multiplayer-online-games-part-arch, and paperback Vol. I got to Amazon a few days ago. A bit of bragging - "Early Praise" page within the book includes references by such people as Glenn Fiedler, Matt Pritchard, two GDC speakers, and CTO-like guys from more than one billion-dollar company...
  4. Database Structure for MMO's

    It's totally normal, if you consider a character as being an entity with a well-defined set of skills. Yes, formally it is all in the definition of the "domain" used in definition of 1NF; however - you will still be pummeled really hard by SQL folks for doing it ;-) (and if, as it was noted somewhere in this thread, there are like 100 skills per player - I won't really blame them too). Overall, personally I'd rather stay away from your approach #1 (column per skill), as writing SELECT and bindings for 100 columns is ugly, and going above 256 columns can get into strange troubles depending on RDBMS, etc. etc. As for #2 (classical relational JOIN) - it is not bad, but from what I've seen, SQL operations on such items (such as average value of the skill you mentioned) are extremely rare, so it tends to lead to unnecessary overstructuring (which has its own costs, both development-wise and runtime-wise). As a result, my order of preference would probably be #3 (BLOB) - though the same SQL folks will beat me too ;-), then #2 (JOIN), then #1 (column per skill). It's interesting that this stands even for large organizations (I have seen a migration from JOIN to BLOBs for a 100M-row table in a game with 500K of simultaneous players, not a startup at any rate...). Just my $0.02 though...
  5. Berkeley Sockets and IOCP

    For a simulation game - it would be more like this (note similarities to the classical game loop):    while ( 1 ) packet = select( sockets, time_until_next_network_tick() ) if( packet ) game_process_packet( packet )//similar to game loop's "process_inputs()" //MAY just store the packet and process within game_update() else//timeout, need to simulate next tick game_update()//similar to game loop's "update()" game_post_updates_to_clients()//similar to game loop's "render()" A potentially-better-performing alternative (assuming that you do NOT really need to process packets until the next tick, and for UDP - make sure to test it for losing extra packets due to stack running out of space while waiting(!) - and play with SO_RCVBUF if necessary) could be  while ( 1 ) sleep( time_until_next_network_tick() ) while( 1 ) //read all the packets from the UDP stack buffer packet = select( sockets, 0 )//timeout is 0, no blocking here! if(packet) game_process_packet( packet )//similar to game loop's "process_inputs()" else break //no more packets - proceed with the tick game_update()//similar to game loop's "update()" game_post_updates_to_clients()//similar to game loop's "render()"
  6. Berkeley Sockets and IOCP

    FWIW: IOCP and non-blocking sockets are quite different architectures, and actually aim different workloads. BTW - we're speaking about servers, right? With IOCP, they maintain a thread pool for you, and process each incoming request in-whatever-thread-is-more-convenient-for-them. It means that IF you want to access some-state-shared-between-different-requests - you DO need an explicit mutex lock within your request handler. IOCP is handy - for those cases when, while processing your requests, you do NOT need to lock on a mutex (or when contention on the mutex/whatever-other-resource is negligible). IOCP is explicitly supported on Win-only, but it is possible to write pretty much the same thing on Linux too. An alternative approach is to dedicate your sockets (like "all the 10 sockets for this MOBA match") to appropriate threads (often, though not strictly necessary - the very same threads which run your game loops) - and to use select()-like function (these days - poll()/epoll()/kqueue()/WaitForMultipleObjects() are superior to select(), but the difference is not interesting at this level) to get the packet as it arrives from any of these sockets (or if nothing arrives - you wait until your next network tick starts). In this case, you do NOT to bother about mutexes and can simply feed the packets from select()-like function into your game loop (it is ok, because everything stays within the same thread). As a rule of thumb - this Shared-Nothing architecture is _superior_ to IOCP-based one at least for games (when applying IOCP to games - it means pretty high contention on the mutexes, causing lots of unnecessary context switching). An anecdotal evidence from the real world. Once upon a time, there was a game with hundreds of thousands of simultaneous players - and it had servers running on Windows, with ~30 non-blocking sockets per each thread (and using WaitForMultipleObjects() to get the packets), and with ~10K players/server. And at some point an experiment was made to rewrite it to IOCP (because everybody was running around saying "IOCP is THE thing, we're stupid not to use it"). As a result - after spending those several weeks necessary to do it, IOCP did work, but performance difference, while being pretty small (~5%), was in favor of the _original_ non-blocking Shared-Nothing implementation.  Bottom line: IMNSHO, while IOCP may be good for web-browser-like type of load (BTW, historically IOCP was developed specifically for IIS), and while it IS possible to write workable stuff based on IOCP, I _strongly_ prefer classical Shared-Nothing non-blocking select()-like-based approach to IOCP; as a side benefit - it works exactly the same way on Linux too (replacing WaitForMultipleObjects() with epoll() is very straightforward).  Phew (sorry if it is a bit sketchy)...   Have to say that my experience is different - see, in particular, that real-world experiment above, plus also there is a plausible theoretical explanation too: Shared-Nothing stuff works better because it causes significantly fewer context switches (context switch, including cache invalidation costs, can cost as much as 1M CPU clock cycles(!)), AND because of better affinity of threads to RAM (which is important for NUMA - and for last 20 years typical "workhorse" servers are 2-socket NUMA boxes). And we didn't even start to discuss stuff such as RSS/RPS/... - where affinity to threads/cores is even more important (but dealing with RSS/RPS/etc. admittedly goes into the realm of black magic). In addition - while IOCP tends to scale worse-than-linear, non-blocking stuff tends to scale better-than-linear(!) - which is a complicated phenomenon which won't fit into the post here. And from a very different perspective - well, Linux is known to scale at least as good as Windows (and without IOCP), which means that IOCP is not really THAT important as MS is trying to tell us :-).   Last but not least on scaling: of course, trying to push _all_ the sockets in one single thread won't scale - but having like 30 sockets/core for 10K players will get us to 300 threads, and 300 threads per 10 cores tends to scale surprisingly well (though probably, the "sweet spot" is even lower - at 50 threads per 10 cores, but this is already game-dependent etc.). 
  7. Hundreds of timers...

        Yep, it is a loooong story. However, writing thread still does NOT need to wait for write to be completed.      Ahh, it depends on the way HOW "memory-bound" your workload is. From what I've seen, real-world workloads (except for DBs) tend to be memory-LATENCY-bound (i.e time while the thread waits for read), rather than memory-THROUGHPUT-bound. Even for DBs I am not 100% sure about it since FSB has been replaced with NUMA 10 years ago...   Alternatively, if we don't want to rely on "whatever I've seen", we can do some math. Xeon E5 can pump up to 100GB/sec at 14 cores (http://www.anandtech.com/show/8423/intel-xeon-e5-version-3-up-to-18-haswell-ep-cores-/10 ) or about 2.5 bytes/core/tick. While it is certainly possible to write a program pumping more, for any kind of real-world load (except, maybe, for DB) such patterns will be quite unusual. Even more so for simulation games which are traditionally do a bit more than just memcpy :-).   And we're back to the square one ;-). In other words - I contend that most of the loads out there are memory-LATENCY-bound, and therefore writes are cheap (and reads are expensive).        As usual :-).
  8. Hundreds of timers...

      Most likely, it will need not only compares, but also conditional jumps, and each conditional jump is a potential pipeline stall, costing around like 10-20 clocks each :-(.          Sure, the only question being "what kind of priority queue we're speaking about". If it is something like std::priority_queue<>, it will get a fair O(log(N)) share of pipeline stalls (and probably cache misses too) on inserts. Linear scan-based stuff will be technically O(N), but will be faster for small Ns due to better prefetching (for integers, below N=50 linear scans will be faster almost for sure, above N=500 - very unlikely, in between - It Depends). For OP's N=5000, I'd probably bet on something along the lines of std::priority_queue<>. Then, my Really Wild guesstimate (give or take an order of magnitude) on insert/extract time would be around 1000 clocks (13 jumps on memory with last 4-5 of them being really close and fitting within the same cache line, plus 13 conditional jumps with (?) half of them causing stalls, plus some other unspecified stuff which always happens). If somebody tests it - please share results as it is always interesting to see how much such estimates are off (though within an order of magnitude it should be fine, I hope) ;-).        While I do wholeheartedly support the idea of using "happening at" semantics for "when it is going to happen" (instead of "happening in" semantics), I have to disagree about write costs. As a rule of thumb (though not without its exceptions), writes are _faster_ than reads. Read from main memory will take 100+ clocks (and L3-L2-L1 reads will take around 40-12-4 clocks respectively); writes on modern CPUs usually (give or take, and NOT when you're writing 100% of the time) are around 1 clock (the write is just stored in hardware and most of the time you don't care when it really reaches cache, or whether it causes cache eviction); sure, there are exceptions (after all, it is possible to overload memory bus, and there are also other considerations like inter-core cache invalidation too), but overall - for a normal program it is reads which are expensive (though once again - I am NOT advocating for unnecessary writes).
  9. Handling "jitter" using TCP for a slowpaced MMO

      IMO this is Very Unlikely in your case (floating point determinism comes into play when ALL the other sources of non-determinism are out, and this doesn't seem to be the case). What you have seems to be a classical problem, which can be seen as a problem of  client-side prediction going out of sync from server-side representation. It is further exacerbated by using TCP, which (in case of packet loss) causes BIG FAT delays. Classical approach in authoritative-server world is simply to send updates to client coordinates more frequently than "when player stops"; normally it is done on each network tick (save for any "dead reckoning" stuff which allows to save on traffic and skip some of the updates), effectively synchronising client with server's representation on each tick (usually synchronisation is not exact, but within a pre-defined margin of error to save on traffic). 
  10. CMB/LBTS protocol for time sync in MMOs?

    Thanks!     These-guys-writing-about-it-in-the-book are from Modelbenders, and the book is 2005, so it may be the same thing. And  of course, they're missing one simple thing: applicability of whatever-is-done-in-one-field to another-related-but-different-field is not guaranteed at all...   BTW, about these books "Massively Multiplayer Game Development" and "Massively Multiplayer Game Development 2" - they're soooo weird mix of Really Good Stuff and utterly inapplicable things (these things tend to read smoothly, but in the first 30 seconds after I lift my eyes from the text and start thinking, it becomes obvious that they cannot work in the game environment at least the way they're described...)
  11. Static, zero-overhead (probably) PIMPL in C++

      IMO move constructor should do the trick. As in - you're creating an object on-stack, then construct Foo<> (wherever you want), which calls move-constructor from your on-stack object within. 
  12. In another book (http://www.amazon.com/Massively-Multiplayer-Development-Charles-River/dp/1584503904) they're arguing for using CMP (Chandy/Misra/Bryant) and LBTS (Lower-Bound-Time-Stamp) time-sync protocols to sync different parts of the same seamless world. On the other hand, CMP/LBTS seem to be of block-until-the-slowest-one-reacts nature, so I have my doubts about using them for games (beyond non-real-time simulations where they're used to guarantee correctness).    Has anybody heard about these algos used in MMOs?
  13. In the book http://www.amazon.com/Multiplayer-Game-Programming-Architecting-Networked/dp/0134034309 , they describe a thing which is essentially a frustum-based interest management. They do acknowledge obvious problems with sharp turns, and suggest two-layer system: (a) for short distances - distance-based, and (b) for longer distances - frustum-based.    I'm wondering - are there any serious games out there doing this kind of stuff?