多线程游戏服务器的基本设计?

发布于 2024-09-25 11:12:01 字数 92 浏览 9 评论 0原文

多线程游戏服务器是如何编写的?

如果有 4 个线程,是否有 1 个线程运行游戏循环,以及 3 个接受和处理请求?另外:信息是从运行游戏循环的线程发送的吗?

How are multithreaded game servers written?

If there are 4 threads, is there one thread running the game loop, and 3 accepting and processing requests? Also: is information sent from the thread running the game loop?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

如果没有 2024-10-02 11:12:01

斯塔基已经指出,这在很大程度上取决于精确的设计。

例如,在具有许多客户端的游戏中,您可以分配专用线程来处理输入,但对于具有少量客户端(例如 <=16)的游戏,则不需要多个线程。

有些游戏的 NPC 非常聪明。在它们自己的线程上运行它们可能是明智之举,但如果您有太多线程,则需要一个线程池,以便一群 NPC 可以共享一个线程。

如果你有一个持久的世界,你需要将状态写到硬盘的某个地方(可能通过数据库)。由于这会产生严重的延迟,因此您不希望主游戏循环等待该 I/O。那将是另一个线程。

最后,还有一个问题:是否有一个游戏循环。 MMO 是否只有一个循环,还是希望有多个循环?

Starkey already pointed out that it depends a whole lot on the precise design.

For instance, on games with many clients, you'd assign dedicated threads to handling input, but for games with a few clients (say <=16) there's no need for multiple threads.

Some games feature NPC's with considerable smarts. It may be smart to run those on their own threads, but if you have too many you'll need a threadpool so a bunch of NPC's can share a single thread.

If you've got a persistent world, you'll need to write out state to a hard disk somewhere (probably via a DB). Since that has serious latencies, you won't want to have a main game loop wait on that I/O. That will be another thread, then.

Finally, there's the question whether you even have a main game loop. Would a MMO have a single loop, or would you rather have many ?

浪菊怪哟 2024-10-02 11:12:01

主要的关键是确保您的游戏逻辑不会受到线程模型的影响。

因此,大多数游戏服务器看起来都是这样的:

main() {

  gGlobalReadOnlyStuff = LoadReadOnlyStuff();

  SpawnThreads(numCores); // could be another limiting resource...
  WaitForThreadsToBeReadyToGo();

  while(1) {
     WaitForNetworkInput(networkInput);

     switch(networkInput.msg) {

     case ADMIN_THING:  // start/stop websever, dump logs, whatever...
          DoAdminThing(networkInput.params);  
          break;

     case SPAWN_GAME: // replace 'game' with 'zone' or 'instance' as needed
          idThread = ChooseBestThread(); // round robin, random, etc
          PostStartGameMessageToThread(idThread, networkInput.msg);
          break;

     // ...

     }
  }

}

void ThreadUpdate() {

   threadLocalStuff = LoadThreadLocalStuff();

   SignalThreadIsReadyToGo();   

   while(1) {

   lock(myThreadsMessageQueue);
   // copy messages to keep lock short
   localMessageQueue = threadsMessageQueue;
   unlock(myThreadsMessageQueue);

   foreach(message in localMessageQueue) {
       switch(message.msg) {
       case SPAWN_GAME:
           threadLocalStuff.games.MakeNewGame(message.params));
           break;
       case ADMIN_THING__LET_EVERYONE_KNOW_ABOUT_SERVER_RESET:
           ...;
           break;
       // etc...
       }
   }


   foreach(game in threadLocalStuff.games) {
       game.Update(); // game will handle its own network communication
   }
}

两个困难的事情是“提出适合你的游戏的分区(游戏、区域、实例等)”和“在游戏中过渡事物(玩家、火球、史诗般的战利品)”这些边界'一个典型的答案是“通过数据库将其序列化”,但您可以使用套接字/消息/文件/任何东西。但是,是的,在哪里以及如何进行这些分区以及最大限度地减少可能跨越边界的内容与您的游戏设计密切相关。

(是的,根据您的设置,可能有一些“共享”系统(日志记录、内存)可能需要多线程处理(或者更好,每个线程只有一个记录器/堆))

The main key is to make sure your game logic is not affected by your threading model.

As such, most game servers look something like this:

main() {

  gGlobalReadOnlyStuff = LoadReadOnlyStuff();

  SpawnThreads(numCores); // could be another limiting resource...
  WaitForThreadsToBeReadyToGo();

  while(1) {
     WaitForNetworkInput(networkInput);

     switch(networkInput.msg) {

     case ADMIN_THING:  // start/stop websever, dump logs, whatever...
          DoAdminThing(networkInput.params);  
          break;

     case SPAWN_GAME: // replace 'game' with 'zone' or 'instance' as needed
          idThread = ChooseBestThread(); // round robin, random, etc
          PostStartGameMessageToThread(idThread, networkInput.msg);
          break;

     // ...

     }
  }

}

void ThreadUpdate() {

   threadLocalStuff = LoadThreadLocalStuff();

   SignalThreadIsReadyToGo();   

   while(1) {

   lock(myThreadsMessageQueue);
   // copy messages to keep lock short
   localMessageQueue = threadsMessageQueue;
   unlock(myThreadsMessageQueue);

   foreach(message in localMessageQueue) {
       switch(message.msg) {
       case SPAWN_GAME:
           threadLocalStuff.games.MakeNewGame(message.params));
           break;
       case ADMIN_THING__LET_EVERYONE_KNOW_ABOUT_SERVER_RESET:
           ...;
           break;
       // etc...
       }
   }


   foreach(game in threadLocalStuff.games) {
       game.Update(); // game will handle its own network communication
   }
}

The two hard things then are 'coming up with a partition (game, zone, instance, whatever) appropriate for you game' and 'transitioning things (players, fireballs, epic lootz) across those boundaries' One typical answer is "serialize it through a database", but you could use sockets/messages/files/whatever. But yeah, where and how to make these partitions and minimizing what can go across the boundaries is intimately tied to your game design.

(And yes, depending on your setup, there are possibly a few 'shared' systems (logging, memory) that may need a multithreading treatment(or even better, just have one logger/heap per thread))

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文