A few conceptual questions (regarding servers)

Started by
7 comments, last by Litz 13 years, 3 months ago
I'm making a 2D ORPG for learning purposes and since I never tried to do something like this before, I have a few questions.

Since I already have plenty of (offline) game experience, the game will be really simple. The purpose of this is to learn the online aspect so I want to build it in a way close to commercial MMOs' servers.

I read some articles and topics about this, and my server architecture is like this:
- Gateway server (forwards packets from clients to other servers, and the other way around).
- Login server
- Database server (this computer would have the actual database, and the idea would be to load the accounts/characters into memory while they were needed, updating the actual database every n hours).
- World server

I'm having some problems understanding something though. Say I want to create a class "Account" and a class "Character", to hold the information from the database (for the logged accounts/characters). Would these classes be on the Database server? Or should the "Account" class be on the Login server and the "Character" class on World server?

At first I thought they would be on the database server since I'll have to load the information there anyways, but I think if that was the case, it would be a bit pointless to have Login/World servers since I'd have to keep updating (and using) the Accounts/Characters on the Database server. However, if the World server had all the Characters logged in on that world, I wouldn't even need to access the Database server until it was time to save/load a character into memory from the database since I could just keep updating the character on the World server.

I'd really appreciate if someone could point me in the right direction. I have all the servers running and connected, and everything related to their communication done but I can't continue my project since I don't want to do it the wrong way.

Thanks in advance.

Advertisement
There are three concepts here:

1) Services that your server/s provide to the clients.
2) Services that you make use of to implement those services.
3) Physical factoring between services.

What you call "servers" really are services, and can all run in the same process if you want, in different processes on the same machine, or on different machines. You may also want to support running multiple instances of the same service in parallel -- for zones for your game, for example.

The odd man out in your list is the database server. This is not typically something you write yourself. Instead, that's an existing server (be it Redis, MySQL, Internet Information Server or IBM DB/2) that you just talk to from your service implementations, using some existing client library.

The main job is to design what data goes where, and what degree of consistency/persistency/latency you need for each piece of data. For example, the position of a player may not need to be very consistent -- if a server crashes, losing half an hour of running across a desert might be OK. Meanwhile, the number of "+10 Super Swords of Doom" items in your inventory is probably something that is more important -- when you and I trade, that should have transactional integrity, so we can't force items to be duplicated by forcing a crash of the server, say.

Player chat may need no persistency at all -- or you may choose to save chat logs somewhere for auditing purposes, if your customer service wants to get into the business of settling disputes based on chat logs, for example. Just make sure your terms of service allow you to save any and all information provided to your service for some amount of time, to be up-front about it.

Thus you might say that player position, amount of hitpoints/mana, etc, are all kept in RAM in the game server service, and checkpointed back to persistent data store once every 30 minutes, plus when the player does something important (dies, changes zones, "binds," whatever). Meanwhile, inventory probably needs to live authoritatively in the actual persistent server.

Once you've broken these things out, the implementation will be a lot simpler. For an example, you can consider a partition between two classes of services: Services where low response time and high throughput are important, and services where transactional integrity is important. For the low response time, write a custom game server (perhaps using UDP?) For the transactional stuff, use an existing application server -- anything like JSPX servlets in Jetty, or PHP pages in Apache, or Webmachine services on Erlang will work. Those services, in turn, talk to your database server, and translate between "game class data" and "data storage" based on what kind of queries you want to run.

It might be helpful to make the client only talk to one physical process at a time. This might be the "game server" for the "zone" that the player is in. That server can then forward any requests that require transactional semantics -- from verifying name/password, to using inventory, to player trade. The benefit of this set-up is that your game server can use pretty much the same application server back-end as the client, so you don't have to worry about state going out of sync -- all state changes flow through the game server to the client at that point.

The transactional stuff is really no different between an RPG and, say, a web site. You can find tons of resources on how to write web services using various technologies on the web. However, the real-time, lightweight data sharing with rules enforcement is mostly custom to games, and has fewer resources. In turn, those resources seldom talk about the transactional server bit, so coming up with a good synthesis of the two is often a lot of heavy work.
enum Bool { True, False, FileNotFound };
Understood everything, thank you very much.

What I was doing was basically have a database server (made by me) connect to a database (using MySQL), and saving whatever was important, so I ended up doing everything there. I'll keep that server for the sole purpose of querying the database and sending the results back to the Login/World server now, and keep the actual logic on those servers.
Quote:Original post by Litz
Understood everything, thank you very much.

What I was doing was basically have a database server (made by me) connect to a database (using MySQL), and saving whatever was important, so I ended up doing everything there. I'll keep that server for the sole purpose of querying the database and sending the results back to the Login/World server now, and keep the actual logic on those servers.


You shouldn't have a service that your services connect to for the sole purpose of being an intermediary to the database. Just have each service ( be it login, world server, NPC AI server, etc. ) connect directly to the database using the native database api.

Database engine vendors ( i.e. MySQL, MSSQL, Oracle, etc. ) spend millions of man hours working on caching algorithms, data integrity, query optimizations, etc. Don't defeat that work by trying to write your own cache in front of the database. Odds are pretty good that whatever you write will not be as good as what the database engine itself can do.

Having your services connect to the database directly is also less work in the long run. Instead of having to write code in the service, that sends a special command to the db front end, which has special code to convert this command into SQL, which then gets the result and has to handle buffering it back to the caller, which then has to interpret and use it; you just issue the SQL command directly from the requesting service and handle the result. This also simplifies debugging as you have simplified the system, making it much easier to trace. You've also made it easier to maintain since you no longer have to change two services to make one change.

The web analogy of your design is a web page that makes a request to another web page on the same host to get data from the database.

On a side note:

Since this is a learning exercise you might want to look into virtualization. With virtualization you can use one piece of hardware to run multiple separate servers. There is no reason your services can't all run on the same host, I just bring this up as a learning tool to make it feel like you have a server cluster. To get your feet wet Virtualbox is quite good. If you have a dedicated system you can use, VMWare ESXi 4.x is also quite good. If you don't have spare hardware, but still want server style virtualization VMWare Server is also pretty good. All of these are free. I plug this as I do this at work to simulate systems ( at low load ) as it is just as easy / cheaper than having physical hardware.

--Z
Quote:Original post by Litz
Understood everything, thank you very much.

What I was doing was basically have a database server (made by me) connect to a database (using MySQL), and saving whatever was important, so I ended up doing everything there. I'll keep that server for the sole purpose of querying the database and sending the results back to the Login/World server now, and keep the actual logic on those servers.


That "database" server/service, if you do "everything" there, ends up being your application server. It's totally fine to put all your transactional processing in that application server. It's less likely you'll be happy if you also put real-time game state in that same application server, but that could also be done (if you go down the route of custom servers for everything).
enum Bool { True, False, FileNotFound };
Litz: I'm starting a similar project myself and was wondering what articles you've read?

Also, I could probably start a separate thread for this, but when using zones how is it tackled if lots of players go into the same zone?

how is it tackled if lots of players go into the same zone?


Several options:

1) Best effort: The more players enter, the more lag there will be.
2) Limitation: You cannot go to a zone that is "full."
3) Instancing: Spin up a new copy of the same zone once the first instance gets to capacity, and send further players entering the zone there.
enum Bool { True, False, FileNotFound };
You shouldn't have a service that your services connect to for the sole purpose of being an intermediary to the database. Just have each service ( be it login, world server, NPC AI server, etc. ) connect directly to the database using the native database api.

I recently had to move to a system much like this and would like to disagree with you on this point. I will agree though that until the game has enough traffic to actually require it, it is overkill, but that design does have some benefits.


Database engine vendors ( i.e. MySQL, MSSQL, Oracle, etc. ) spend millions of man hours working on caching algorithms, data integrity, query optimizations, etc. Don't defeat that work by trying to write your own cache in front of the database. Odds are pretty good that whatever you write will not be as good as what the database engine itself can do.

Although very true that databases have a low level caching system and it is very effective. The problem is that it is usually not enough. These low level caches are flushed very easily. For example a database caching the result of a query for data related to a user's character. After the first query subsequent queries will be cached only for as long as the table that the data resides in is not changed. The second a new user signs up the cache is flushed for all characters because that query could potentially return different results. Although at a high level these two operations are not related at all.

There's also the case that the low level database cache is keeping all data related to a query cached when you may only need a small portion thus allowing more space to cache more results.


Having your services connect to the database directly is also less work in the long run. Instead of having to write code in the service, that sends a special command to the db front end, which has special code to convert this command into SQL, which then gets the result and has to handle buffering it back to the caller, which then has to interpret and use it; you just issue the SQL command directly from the requesting service and handle the result. This also simplifies debugging as you have simplified the system, making it much easier to trace. You've also made it easier to maintain since you no longer have to change two services to make one change.

Although arguing the other way having a database application can simplify as you scale out your database. Not only does it manage caching but i can also manage connecting to a horizontally partitioned database and any master or slave servers that each may have. This allows you to only mange these connections in one application when scaling the database.

In my own case we have an application layer acting as the database that communicates with multiple MySQL and Memcached servers. This database application is used both for the game and the website so caching is the same between the two. Of course the game servers do their own in memory caching as well. For example, collecting gold on the ground as you're running is not immediately committed to the database layer as you pick them up but added together and updated a small time later as one transaction.
Thanks for the help everyone, I got everything up and running now.


Litz: I'm starting a similar project myself and was wondering what articles you've read?

Also, I could probably start a separate thread for this, but when using zones how is it tackled if lots of players go into the same zone?


I didn't save the links, sorry. I think some were from here and gamasutra. The rest were found in posts/browsing.

As for the zones, I'm not limiting or instancing anything but you can do that if your game allows it. If you limit the amount of people logged in per server, it should be fine (unless everyone decides to camp at the same spot - which probably means something is wrong about that spot).

This topic is closed to new replies.

Advertisement