Unity Multiplayer/MMO Game – Game Devlog #3


My technical thoughts on building a MMO Game. I have no professional experience in this area, and try to come up with an architecture that is simple to implement but also scalable enough for my needs.

00:00 – Intro
01:00 – 1. Using a Networking Provider?
02:19 – 2. Language for Game Server
03:29 – 3. Server Architecture
08:01 – 4. UDP vs. TCP
09:50 – 7. The Network Protocol
10:25 – 8. Position Packet
11:48 – 8. Login Packet
14:11 – 10. UDP Reflection Attack
16:12 – 11. UDP Client in C#
20:59- 12. UDP Server in Python
23:48 – 14. Distributing Player Position
25:58 – 15. HTTP Server Component
26:49 – Conclusion
27:00 – Outro

Part 1 – How To Learn Something New?:
Part 2 – Igniting Creativity:
Part 3 – Unity Multiplayer/MMO Game: this video

Heatmap Animation:

-=[ 🔴 Stuff I use ]=-

→ Microphone:*
→ Graphics tablet:*
→ Camera#1 for streaming:*
→ Lens for streaming:*
→ Connect Camera#1 to PC:*
→ Keyboard:*
→ Old Microphone:*

US Store Front:*

-=[ ❤️ Support ]=-

→ per Video:
→ per Month:

-=[ 🐕 Social ]=-

→ Twitter:
→ Website:
→ Subreddit:
→ Facebook:

-=[ 📄 P.S. ]=-

All links with “*” are affiliate links.
LiveOverflow / Security Flag GmbH is part of the Amazon Affiliate Partner Programm.

Nguồn: https://onvideonews.com/

Xem thêm bài viết khác: https://onvideonews.com/game/

22 thoughts on “Unity Multiplayer/MMO Game – Game Devlog #3

  1. Hello, Game Server-side dev here 🙂
    I noticed some flaws in your designs, and I wonder why you didn't learned by using resources available online (for example, Quake Network Protocol, which is open-source, Valve protocol, etc.), it could make you gain a massive time on some of the problems you faced 🙂
    First of all, lets discuss about your choices:
    – 1 Thread per client (also called "Fork design", design used by Apache Web Server for example) vs Worker Threads (design used by Nginx):
    In most cases, when you want to handle a massive amount of clients on the same server, a design where 1 thread is designated per client is a bad idea. Most async models will use a pool of threads for write and reading operations ! The most common usage is to use a Boss Thread Group (which is responsible for reading and writing to the sockets) and some Worker Group (Which are used to handle the heavy load operations).
    The most common design in gaming is to use the Select (or epoll if possible) to have either one thread only (the order would be: Read packets -> Game Loop -> Send game state packets) or to use, as stated before, a Thread-worker-queue model (Using the Boss Thread Group to read the data and deserialize it then add it to a queue that will be processed by your game loop at the start of each loop).
    The model you choose is probably, as you guessed it, why some packets where not sent to your clients !
    Fork design was heavily used before the existence of "async" and "await" models, because they can provide a reliable way to order data. It's not used anymore because of the limitations it has (Kernel can limit Forks/Threads, switching context costs massive CPU-Time, etc.)
    – UDP vs TCP:
    As you stated, it highly depends on your needs. UDP Has the advantage to be very modular and controlable, but it's very complicated to handle properly: Security problems, ACK management in some cases, etc.
    I will talk after how to tackle this kind of points, but here, let's just talk about the advantages and disadvantages of both transports.
    The biggest advantage of UDP is, of course, its Speed (3 to 8x faster than TCP in general), which is proven to be very effective for real-time shooters for example. The less you will have delay between your client and server answers, less you will have unfair ping advantages for your players.
    TCP in another hand is very practical: Because everything is checked for you, you don't have to manage anything very important regarding packet ACK and hackers stealing your IP. The only real disadvantage of TCP is to have an open connection (which prevents for reflection attacks)+ and a very low speed. (Open connection, for simply pinging a server for example, is overkill. That's way most "heartbeat" protocols (from master servers to game servers, master servers is what allows you to find servers on CS for example) will not use a connection-based protocol. It's not very useful at this state to open a complete connection.
    Let's now talk about the protocol choices:
    – Login packet:
    The idea behind sending the "identification key" in each packet is interesting, but that means that an attacker can steal your key ! A common practice is to have the key be generated by using the user's credentials (as you've done in your game) and by signing important packets using this key.
    For example, a login handshake can be done like this:
    Client sends a login request => Server responds with a special key generated using the current timestamp => Client uses this key to generate its own private key, which be used to sign packets using its own credentials => Client sends to the server its login (NOT THE PASSWORD, ONLY AN IDENTIFIER) and signs it => The server can now check the packet by checking if the identifier challenge corresponds to the signature generated by the client. Tadaaaaa.
    Using this method, you can sign your important packets (be careful, because signing has a cost in CPU-TIME !) and provide a way to transmit packets WITHOUT assigning them a key that can be stolen !
    This method is also used in TCP too, to avoid having to encrypt all the data sent and avoid having credentials thrown on the network.
    – Position packet:
    Finally, one of the most complicated packets to handle in games in general.
    The methods you described (Send position, send delta or send keys) are respectively named: Client-Authorative and Server Authorative (in general, in this model, we will send the keys AND the delta at the same time).
    Client-Authorative ways are simple to implement: Simply accept the position provided by your clients, and interpolate on other clients. BUT, it's highly hackable or it will demand a massive amount of work on the server-side and massive checks to avoid hacks (which will cost dev time and CPU-Time).
    The most commonly used approach is the Server-Authorative approach with client-side prediction. In this case, the client will send its inputs, the last snapshot ID received from the server (I will talk about this after, but to be simple, its the "current view" of the client) and the delta between the moment where you sent your input and the last snapshot received. This will allow the server to accurately execute your position and will not be affected by the client part. In this way, the client cannot cheat, because the position is handled by the server, and has the exact same way to calculate the position than the client-side. The client will predict its position (that means that, when it will send the keys packet, it will also start moving the local character) and apply what we call "Server Reconciliation" where the client will check what the server is sending him back as a position, and correct itself depending on this data.
    And, another very important thing: Your way of doing things can induce rubberbanding !
    Do not send ONE PACKET per client position update ! If there are too many clients, you will overuse your clients CPU power and your bandwidth very quickly.
    The best way to handle this is to send what we call a "snapshot". Instead of sending one position packet per client, you will send a big packet containing all the positions of your clients. This has also the advantage to provide a reliable way to manage the snapshot ID which can be used for client prediction and entity interpolation !
    I will provide various documentations about this bellow !
    – One last point was your way to manage multiple instances of your game, because you have no interaction between your players, the model was not very relevant, but be careful with it ! I've added some design patterns commonly used in distributed game servers bellow :), the problem in your model is that, even if you are sharing your players between multiple instances, you are still sending them the positions of others players (which means that your server are still managing too many data that can be used more cleverly by using spacial hashing techniques etc.), I think that your model will handle more players than a single server model, but they will very fastly DDoS themselves with the player positions updates 🙂
    I will not talk about your way to manage game events (using an image is a good way to do it if you have a very limited time, but it's not a very good long-term solution), I'm more interested about the network-part of things 🙂
    Here are some technical documentation if you are interested about this a little more:
    – https://developer.valvesoftware.com/wiki/Source_Multiplayer_Networking
    – https://www.gabrielgambetta.com/client-server-game-architecture.html
    – https://gameserverarchitecture.com/2016/03/pattern-seamless-world-game-server/
    – And a list of useful real-world resources that I made when I was doing my thesis on multiplayer games models: https://docs.google.com/document/d/1TKqoxa4XEnuxDI8qfP1dEEFiorEg94JA4FU6YNylyPQ/edit?usp=sharing
    Hope my comment will help others to have some learning resources on Networking in games !
    Happy Learning 😀

  2. where do I find files edited by tools such as cheat engine or gameguardian on Android, and how do I see package data transfers in online games? tutorial please

  3. "username is limited to 32 bytes" looks like you can inject a longer username to me. Add a split(0,31) for username.length>32 before it's put into the packet.It's still possible to overflow by debugging and skipping that check in assembly, but still much harder. anti-hacking is mostly just about making effort required to hack the target process more than the perceived reward.

  4. every "mmo" style gameserver I'ce ever made (going all the way back to VB.6 days) I've had {clients} <-> [x]MMs <-> As where x is the server id, MMs is middle-man-server and As = actual server. Essentially, instanced server, one database. Pretty much replicating runescape's server structure. your client[x]<->serverProcess[x]<->ServerProcess structure is interesting though

  5. Thank you for making this very informative and interesting video! It was very fun to watch and I hope you continue making cool videos like these

  6. You should really check out GafferOnGames, it has a very detailed explanation why you would want to use UDP for real-time networking. And how to work around the shortcomings of UDP. I understand the lack of time but i should still use UDP definitely not TCP for a real-time game.

  7. On one application I open multiple TCP streams and send the data on them all. This increases traffic, but gives redundancy if one packet is lost and one TCP stream stalls. I tried a UDP system with FEC(forward error correction), but I found that sending multiple UDP packets at the same time caused them all to be lost. UDP packets had to be sent at a precise time and that was hard from even a user mode C++ program that had boosted thread priority.

  8. So that's why the server was preventing me from walking over the walls – it had the whole maze as a 2D picture!
    Although eventually I did find away to teleport "through" the walls, but mostly used it while standing on top of them, for better visibility.
    Even got outside of the maze once. Not much to do there, except that walking on the outer edge of the outermost wall is the easiest way to get from one side of the maze to another.

  9. Hey! I see a lot of people giving their experiences in the comments. I would like to give a different piece of evidence : orders of magnitudes. You had a lot of questions about bottlenecks and I think that a lot of your questions could be solved by a simple order of magnitude math which is really well explained here (look at the talk linked there) : https://github.com/graydon/napkin-math

  10. Where you are checking if the player moves to fast (within the serverscript) you could square 100000 instead of square rooting distance, which would allow you to save expensive square rooting operations. You could do the same for the speed check. Lovely video

  11. One bit of small feedback as well is about message encoding. I've noticed in the client that there's a lot of code to parse each packet byte by byte etc. This wastes a lot of time, error prone and most importantly hard to get backward comptability righ if you ever want to change anything in the protocol. Instead of you implementing your own binary protocol, you could use something like protobuf (https://developers.google.com/protocol-buffers) or thrift (https://thrift.apache.org/). In protobuf/thrift you define your structures in a language agnostic DSL and then they take care of generating serializers/deserializers for your messages in multiple different languages. Adding/removing fields is then handled by those libraries as long as you follow the rule for modifying your definitions. I think this could make your life much much easier. Hope this was useful! Great series btw, I love it!

  12. very cool – i'd replace long chains of unsightly if() {} else if () {} instead with the switch statement.

Leave a Reply

Your email address will not be published. Required fields are marked *